diff --git a/README.md b/README.md index f5dad4019..dc1a46139 100644 --- a/README.md +++ b/README.md @@ -4,10 +4,210 @@ ## 목차 [* 코드 소개](#코드-소개) [* 0 단계 - 기본 코드 준비](#-0-단계----기본-코드-준비) +[* 1 단계 - API 명세](#-1-단계----api-명세) +[* 2 단계 - 배포하기](#-2-단계----배포하기) ## 코드 소개 카카오 선물하기의 상품을 관리하는 서비스를 구현해본다 ## < 0 단계 > - 기본 코드 준비 ### [ 기능 요구 사항 ] -- [spring-gift-order](https://github.com/chris0825/spring-gift-order.git)의 코드를 옮겨온다. \ No newline at end of file +- [spring-gift-order](https://github.com/chris0825/spring-gift-order.git)의 코드를 옮겨온다. + +## < 1 단계 > - API 명세 +### [ 기능 요구 사항 ] +> 작성한 API 문서를 기반으로 팀 내에서 지금까지 만든 API를 검토하고 통일하여 변경 사항을 반영한다. +- 팀 내에서 일관된 기준을 정하여 API 명세를 결정한다. +- 때로는 클라이언트의 편의를 위해 API 명세를 결정하는 것이 좋다. + +### [ 연동 API 명세 ] + + 로그인 API + +### 카카오 로그인 및 회원 가입 (Authorize code & access token) +- request +> GET /kakao/login HTTP/1.1 +- rseponse +```json +{ + "token": "String" +} +``` +### 로그인 이후 HTTP 헤더에 로그인 시 발급된 토큰을 포함한 인증 헤더가 존재하여야 한다. +> Authorization: Bearer {token} + + + 카테고리 API + +### 카테고리 목록 조회 +- request +> GET /api/categories HTTP/1.1 +- response +```json +HTTP/1.1 200 OK +Content-Type: application/json +[ + { + "id": Long, + "name": "String", + "color": "String", + "imageUrl": "String", + "description": "String" + } +] +``` + + + 상품 API + +### 상품 상세 조회 +- request +> GET /api/products/{productId} HTTP/1.1 +- response +```json +HTTP/1.1 200 OK +Content-Type: application/json +{ + "name": "String", + "price": "int", + "imageUrl": "String", + "categoryId": "Long" +} +``` +### 전체 상품 목록 조회 +- request +> GET /api/products?page=0&size=10&sort=name,asc&categoryId=1 HTTP/1.1 +- response +```json +HTTP/1.1 200 OK +Content-Type: application/json +[ + { + "id": Long, + "name": "String", + "price": "int", + "imageUrl": "String", + "categoryId": "Long" + } +] +``` + + + 옵션 API + +### 상품 하위 옵션 조회 +- request +> GET /api/products/{productId}/options HTTP/1.1 +- response +```json +HTTP/1.1 200 OK +Content-Type: application/json +[ + { + "id": "Long", + "name": "String", + "quantity": "int" + } +] +``` + + + 위시 API + +### 위시 상품 추가 +- request +```json +POST /api/wishes HTTP/1.1 +Content-Type: application/json +Authorization: Bearer {token} +{ + "productId": "Long" +} +``` +- response +```json +HTTP/1.1 201 CREATED +Content-Type: application/json +{ + "productId": "Long" +} +``` +### 위시 상품 삭제 +- request +> DELETE /api/wishes/{productId} HTTP/1.1 +- response +```json +HTTP/1.1 204 NO_CONTENT +``` +### 위시 리스트 조회 +- request +> GET /api/wishes?page=0&size=10&sort=createdDate,desc HTTP/1.1 +- response +```json +HTTP/1.1 200 OK +Content-Type: application/json +[ + { + "id": "Long", + "product": { + "id": "Long", + "name": "String", + "price": "int", + "imageUrl": "String" + } + } +] +``` + + + 주문 API +### 주문하기 +- request +```json +POST /api/orders HTTP/1.1 +Content-Type: application/json +Authorization: Bearer {token} +{ + "optionId": "Long", + "quantity": "int", + "message": "String" +} +``` +- response +```json +HTTP/1.1 200 OK +Content-Type: application/json +{ + "id": "Long", + "optionId": "Long", + "quantity": "int", + "orderDateTime": "YYYY-MM-DDTHH:MM:SS", + "message": "String" +} +``` +### 주문 목록 조회 +- request +> GET /api/orders?page=0&size=10&sort=orderDateTime,desc HTTP/1.1 +- response +```json +HTTP/1.1 200 OK +Content-Type: application/json +[ + { + "id": "Long", + "optionId": "Long", + "quantity": "int", + "orderDateTime": "YYYY-MM-DDTHH:MM:SS", + "message": "String" + } +] +``` + + +## < 2 단계 > - 배포하기 +### [ 기능 요구 사항 ] +> 지금까지 만든 선물하기 서비스를 배포하고 클라이언트와 연동할 수 있어야 한다. +- 지속적인 배포를 위한 배포 스크립트를 작성한다. +- 클라이언트와 API 연동 시 발생하는 보안 문제에 대응한다. + - 서버와 클라이언트의 Origin이 달라 요청을 처리할 수 없는 경우를 해결한다. +- HTTPS는 필수는 아니지만 팀 내에서 논의하고 필요한 경우 적용한다. \ No newline at end of file diff --git a/scripts/distribution.sh b/scripts/distribution.sh new file mode 100644 index 000000000..b7e9f8564 --- /dev/null +++ b/scripts/distribution.sh @@ -0,0 +1,20 @@ +#!/bin/bash +BUILD_PATH=$(ls /home/ubuntu/spring-gift-point/build/libs/*.jar) +JAR_NAME=$(basename $BUILD_PATH) + +CURRENT_PID=$(pgrep -f $JAR_NAME) + +if [ -z $CURRENT_PID ] +then + sleep 1 +else + kill -15 $CURRENT_PID + sleep 5 +fi + +DEPLOY_PATH=/home/ubuntu/spring-gift-point/ +cp $BUILD_PATH $DEPLOY_PATH +cd $DEPLOY_PATH + +DEPLOY_JAR=$DEPLOY_PATH$JAR_NAME +nohup java -jar $DEPLOY_JAR > /dev/null 2> /dev/null < /dev/null & \ No newline at end of file diff --git a/src/main/java/gift/product/config/WebMvcConfig.java b/src/main/java/gift/product/config/WebMvcConfig.java index 480b1591f..715720a6f 100644 --- a/src/main/java/gift/product/config/WebMvcConfig.java +++ b/src/main/java/gift/product/config/WebMvcConfig.java @@ -3,6 +3,7 @@ import gift.product.intercepter.AuthInterceptor; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Configuration; +import org.springframework.web.servlet.config.annotation.CorsRegistry; import org.springframework.web.servlet.config.annotation.InterceptorRegistry; import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; @@ -16,7 +17,18 @@ public class WebMvcConfig implements WebMvcConfigurer { public void addInterceptors(InterceptorRegistry registry) { registry.addInterceptor(authInterceptor) .addPathPatterns("/api/**") - .excludePathPatterns("/api/kakao/**", "/api/member/**"); + .excludePathPatterns( + "/api/kakao/**", + "/api/member/**", + "/api/products/**", + "/api/categories/**"); } + @Override + public void addCorsMappings(CorsRegistry registry) { + registry.addMapping("/**") + .allowedOriginPatterns("*") + .allowedMethods("*") + .allowCredentials(true); + } } diff --git a/src/main/java/gift/product/controller/AdminCategoryController.java b/src/main/java/gift/product/controller/AdminCategoryController.java index 93a37d5e0..77c111bc9 100644 --- a/src/main/java/gift/product/controller/AdminCategoryController.java +++ b/src/main/java/gift/product/controller/AdminCategoryController.java @@ -1,7 +1,6 @@ package gift.product.controller; import gift.product.dto.CategoryDTO; -import gift.product.model.Category; import gift.product.service.CategoryService; import jakarta.validation.Valid; import org.springframework.beans.factory.annotation.Autowired; @@ -18,7 +17,7 @@ import org.springframework.web.bind.annotation.RequestMapping; @Controller -@RequestMapping("/admin/category") +@RequestMapping("/admin/categories") public class AdminCategoryController { private final CategoryService categoryService; @@ -31,7 +30,7 @@ public AdminCategoryController(CategoryService categoryService) { @GetMapping public String category(Model model, Pageable pageable) { model.addAttribute("categoryList", categoryService.findAllCategory(pageable)); - return "category"; + return "category-management"; } @GetMapping("/register") @@ -42,7 +41,10 @@ public String registerCategoryForm(Model model) { } @PostMapping - public String registerCategory(@Valid @ModelAttribute CategoryDTO categoryDTO, BindingResult bindingResult, Model model) { + public String registerCategory( + @Valid @ModelAttribute CategoryDTO categoryDTO, + BindingResult bindingResult, + Model model) { System.out.println("[AdminCategoryController] registerCategory()"); if(bindingResult.hasErrors()) { System.out.println("[AdminCategoryController] registerCategory(): validation error: " + bindingResult.getAllErrors() + ", categoryDTO: " + categoryDTO.getName()); @@ -50,7 +52,7 @@ public String registerCategory(@Valid @ModelAttribute CategoryDTO categoryDTO, B return "category-form"; } categoryService.registerCategory(categoryDTO.convertToDomain()); - return "redirect:/admin/category"; + return "redirect:/admin/categories"; } @GetMapping("/{id}") @@ -66,7 +68,7 @@ public String updateCategory(@PathVariable Long id, @Valid @ModelAttribute Categ if(bindingResult.hasErrors()) { model.addAttribute("categoryDTO", categoryDTO); } - categoryService.updateCategory(new Category(id, categoryDTO.getName())); + categoryService.updateCategory(categoryDTO.convertToDomain(id)); return "redirect:/admin/category"; } diff --git a/src/main/java/gift/product/controller/AdminOptionController.java b/src/main/java/gift/product/controller/AdminOptionController.java index bda43f049..749e5f9aa 100644 --- a/src/main/java/gift/product/controller/AdminOptionController.java +++ b/src/main/java/gift/product/controller/AdminOptionController.java @@ -9,6 +9,7 @@ import org.springframework.stereotype.Controller; import org.springframework.ui.Model; import org.springframework.validation.BindingResult; +import org.springframework.web.bind.annotation.DeleteMapping; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.ModelAttribute; import org.springframework.web.bind.annotation.PathVariable; @@ -17,7 +18,7 @@ import org.springframework.web.bind.annotation.RequestMapping; @Controller -@RequestMapping("/admin/product/{productId}/option") +@RequestMapping("/admin/products/{productId}/options") public class AdminOptionController { private final OptionService optionService; @@ -48,32 +49,52 @@ public String showOptionRegisterForm(@PathVariable Long productId, Model model) } @PostMapping - public String registerOption(@PathVariable Long productId, @Valid @ModelAttribute OptionDTO optionDTO, BindingResult bindingResult, Model model) { + public String registerOption( + @PathVariable Long productId, + @Valid @ModelAttribute OptionDTO optionDTO, + BindingResult bindingResult, + Model model) { System.out.println("[AdminOptionController] registerOption()"); if (bindingResult.hasErrors()) { + System.out.println("registerOption(): validation error: " + bindingResult.getAllErrors()); model.addAttribute("option", optionDTO); return "product-option-form"; } optionService.registerOption(productId, optionDTO); - return "redirect:/admin/product/" + productId + "/option"; + return "redirect:/admin/products/" + productId + "/options"; } @GetMapping("/{optionId}") - public String showOptionUpdateForm(@PathVariable Long productId, @PathVariable Long optionId, Model model) { + public String showOptionUpdateForm( + @PathVariable Long productId, + @PathVariable Long optionId, + Model model) { System.out.println("[AdminOptionController] showOptionUpdateForm()"); model.addAttribute("option", optionService.findById(optionId)); return "product-option-update-form"; } @PutMapping("/{optionId}") - public String updateOption(@PathVariable Long optionId, @PathVariable Long productId, @Valid @ModelAttribute OptionDTO optionDTO, BindingResult bindingResult, Model model) { + public String updateOption( + @PathVariable Long optionId, + @PathVariable Long productId, + @Valid @ModelAttribute OptionDTO optionDTO, + BindingResult bindingResult, + Model model) { System.out.println("[AdminOptionController] updateOption()"); if(bindingResult.hasErrors()) { model.addAttribute("option", optionDTO); return "product-option-update-form"; } optionService.updateOption(optionId, productId, optionDTO); - return "redirect:/admin/product/" + productId + "/option"; + return "redirect:/admin/products/" + productId + "/options"; + } + + @DeleteMapping("/{optionId}") + public String deleteOption(@PathVariable Long optionId, @PathVariable Long productId) { + System.out.println("[AdminOptionController] deleteOption()"); + optionService.deleteOption(optionId, productId); + return "redirect:/admin/products/" + productId + "/options"; } } diff --git a/src/main/java/gift/product/controller/AdminProductController.java b/src/main/java/gift/product/controller/AdminProductController.java index 0c9423d8d..0fe3085ad 100644 --- a/src/main/java/gift/product/controller/AdminProductController.java +++ b/src/main/java/gift/product/controller/AdminProductController.java @@ -36,7 +36,7 @@ public AdminProductController( this.categoryService = categoryService; } - @GetMapping("/list") + @GetMapping public String showProductList(Model model, Pageable pageable) { System.out.println("[ProductController] showProductList()"); model.addAttribute("productList", productService.getAllProducts(pageable)); @@ -60,7 +60,7 @@ public String registerProduct(@Valid @ModelAttribute ProductDTO productDTO, Bind return "product-form"; } productService.registerProduct(productDTO); - return "redirect:/admin/product/list"; + return "redirect:/admin/products"; } @GetMapping("/{id}") @@ -81,14 +81,14 @@ public String updateProduct(@PathVariable Long id, @ModelAttribute ProductDTO pr return "product-form"; } productService.updateProduct(id, productDTO); - return "redirect:/admin/product/list"; + return "redirect:/admin/products"; } @DeleteMapping("/{id}") public String deleteProduct(@PathVariable Long id, Model model) { System.out.println("[ProductController] deleteProduct()"); productService.deleteProduct(id); - return "redirect:/admin/product/list"; + return "redirect:/admin/products"; } @GetMapping("/search") diff --git a/src/main/java/gift/product/controller/ApiCategoryController.java b/src/main/java/gift/product/controller/ApiCategoryController.java index 5efb9925d..b55724ac0 100644 --- a/src/main/java/gift/product/controller/ApiCategoryController.java +++ b/src/main/java/gift/product/controller/ApiCategoryController.java @@ -4,12 +4,14 @@ import gift.product.dto.CategoryDTO; import gift.product.model.Category; import gift.product.service.CategoryService; -import io.swagger.v3.oas.annotations.tags.Tag; import jakarta.validation.Valid; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; import org.springframework.http.HttpStatus; 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; @@ -18,7 +20,7 @@ import org.springframework.web.bind.annotation.RestController; @RestController -@RequestMapping("/api/category") +@RequestMapping("/api/categories") public class ApiCategoryController implements CategoryControllerDocs { private final CategoryService categoryService; @@ -35,6 +37,12 @@ public ResponseEntity registerCategory(@Valid @RequestBody CategoryDTO return ResponseEntity.status(HttpStatus.CREATED).body(category); } + @GetMapping + public Page researchCategory(Pageable pageable) { + System.out.println("[CategoryController] researchCategory()"); + return categoryService.findAllCategory(pageable); + } + @PutMapping("/{id}") public ResponseEntity updateCategory(@PathVariable Long id, @Valid @RequestBody CategoryDTO categoryDTO) { System.out.println("[CategoryController] updateCategory()"); diff --git a/src/main/java/gift/product/controller/ApiOrderController.java b/src/main/java/gift/product/controller/ApiOrderController.java index cbc2478d7..405cec77c 100644 --- a/src/main/java/gift/product/controller/ApiOrderController.java +++ b/src/main/java/gift/product/controller/ApiOrderController.java @@ -9,10 +9,13 @@ import jakarta.validation.Valid; import jakarta.validation.ValidationException; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.validation.BindingResult; import org.springframework.validation.FieldError; +import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestHeader; @@ -45,4 +48,11 @@ public ResponseEntity orderProduct( return ResponseEntity.status(HttpStatus.CREATED) .body(orderService.orderProduct(authorization, orderRequestDTO)); } + + @GetMapping + public Page researchOrderList( + @RequestHeader("Authorization") String authorization, + Pageable pageable) { + return orderService.getAllOrders(authorization, pageable); + } } diff --git a/src/main/java/gift/product/controller/ApiProductController.java b/src/main/java/gift/product/controller/ApiProductController.java index cc8e66514..57328da1d 100644 --- a/src/main/java/gift/product/controller/ApiProductController.java +++ b/src/main/java/gift/product/controller/ApiProductController.java @@ -36,7 +36,7 @@ public ApiProductController(ProductService productService) { this.productService = productService; } - @GetMapping("/list") + @GetMapping public Page showProductList(Pageable pageable) { System.out.println("[ProductController] showProductList()"); return productService.getAllProducts(pageable); @@ -57,9 +57,9 @@ public ResponseEntity registerProduct( .body(productService.registerProduct(productDTO)); } - @PutMapping("/{id}") + @PutMapping("/{productId}") public ResponseEntity updateProduct( - @PathVariable Long id, + @PathVariable Long productId, @Valid @RequestBody ProductDTO productDTO, BindingResult bindingResult) { System.out.println("[ProductController] updateProduct()"); @@ -70,7 +70,7 @@ public ResponseEntity updateProduct( throw new ValidationException(UNKNOWN_VALIDATION_ERROR); } return ResponseEntity.status(HttpStatus.CREATED) - .body(productService.updateProduct(id, productDTO)); + .body(productService.updateProduct(productId, productDTO)); } @DeleteMapping("/{id}") diff --git a/src/main/java/gift/product/controller/ApiWishListController.java b/src/main/java/gift/product/controller/ApiWishListController.java index 02d0325cb..3a64b3722 100644 --- a/src/main/java/gift/product/controller/ApiWishListController.java +++ b/src/main/java/gift/product/controller/ApiWishListController.java @@ -20,7 +20,7 @@ import org.springframework.web.bind.annotation.RestController; @RestController -@RequestMapping("/api/wishlist") +@RequestMapping("/api/wishes") public class ApiWishListController implements WishListControllerDocs { private final WishListService wishListService; @@ -47,12 +47,12 @@ public ResponseEntity registerWishProduct( .body(wishListService.registerWishProduct(authorization, wishRequestDTO)); } - @DeleteMapping("/{id}") + @DeleteMapping("/{productId}") public ResponseEntity deleteWishProduct( @RequestHeader("Authorization") String authorization, - @PathVariable Long id) { + @PathVariable Long productId) { System.out.println("[ApiWishListController] deleteWishProduct()"); - wishListService.deleteWishProduct(authorization, id); + wishListService.deleteWishProduct(authorization, productId); return ResponseEntity.status(HttpStatus.NO_CONTENT).build(); } } diff --git a/src/main/java/gift/product/controller/KakaoController.java b/src/main/java/gift/product/controller/KakaoController.java index 279729d9f..c21bdf6c4 100644 --- a/src/main/java/gift/product/controller/KakaoController.java +++ b/src/main/java/gift/product/controller/KakaoController.java @@ -4,9 +4,11 @@ import org.springframework.http.ResponseEntity; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; @Controller +@RequestMapping("/kakao") public class KakaoController { private final KakaoService kakaoService; @@ -15,12 +17,12 @@ public KakaoController(KakaoService kakaoService) { this.kakaoService = kakaoService; } - @GetMapping("/kakao/login") + @GetMapping("/login") public String login() { return "redirect:" + kakaoService.getAuthCode(); } - @GetMapping(params = "code") + @GetMapping("/callback") public ResponseEntity handleKakaoCallback(@RequestParam String code) { return ResponseEntity.ok(kakaoService.login(code)); } diff --git a/src/main/java/gift/product/dto/CategoryDTO.java b/src/main/java/gift/product/dto/CategoryDTO.java index 26bae7f23..6d39afdc9 100644 --- a/src/main/java/gift/product/dto/CategoryDTO.java +++ b/src/main/java/gift/product/dto/CategoryDTO.java @@ -5,26 +5,79 @@ public class CategoryDTO { - @NotBlank(message = "카테고리 이름은 필수 입력사항입니다.") + @NotBlank(message = "카테고리 이름은 필수 입력사항 입니다.") private String name; + @NotBlank(message = "카테고리 색상은 필수 입력사항 입니다.") + private String color; + + @NotBlank(message = "카테고리 이미지는 필수 입력사항 입니다.") + private String imageUrl; + + @NotBlank(message = "카테고리 설명은 필수 입력사항 입니다.") + private String description; + public CategoryDTO() {} - public CategoryDTO(String name) {} + public CategoryDTO(String name, String color, String imageUrl, String description) { + this.name = name; + this.color = color; + this.imageUrl = imageUrl; + this.description = description; + } - public String getName() { + public @NotBlank(message = "카테고리 이름은 필수 입력사항 입니다.") String getName() { return name; } - public void setName(String name) { + public void setName( + @NotBlank(message = "카테고리 이름은 필수 입력사항 입니다.")String name) { this.name = name; } + public @NotBlank(message = "카테고리 색상은 필수 입력사항 입니다.") String getColor() { + return color; + } + + public void setColor( + @NotBlank(message = "카테고리 색상은 필수 입력사항 입니다.") String color) { + this.color = color; + } + + public @NotBlank(message = "카테고리 이미지는 필수 입력사항 입니다.") String getImageUrl() { + return imageUrl; + } + + public void setImageUrl( + @NotBlank(message = "카테고리 이미지는 필수 입력사항 입니다.") String imageUrl) { + this.imageUrl = imageUrl; + } + + public @NotBlank(message = "카테고리 설명은 필수 입력사항 입니다.") String getDescription() { + return description; + } + + public void setDescription( + @NotBlank(message = "카테고리 설명은 필수 입력사항 입니다.") String description) { + this.description = description; + } + public Category convertToDomain() { - return new Category(this.name); + return new Category( + this.name, + this.color, + this.imageUrl, + this.description + ); } public Category convertToDomain(Long id) { - return new Category(id, this.name); + return new Category( + id, + this.name, + this.color, + this.imageUrl, + this.description + ); } } diff --git a/src/main/java/gift/product/dto/OrderRequestDTO.java b/src/main/java/gift/product/dto/OrderRequestDTO.java index c0eaac47b..d2f359a22 100644 --- a/src/main/java/gift/product/dto/OrderRequestDTO.java +++ b/src/main/java/gift/product/dto/OrderRequestDTO.java @@ -34,12 +34,12 @@ public void setMessage(String message) { this.message = message; } - public Order convertToDomain(Option option, Member orderer) { + public Order convertToDomain(Option option, Member member) { return new Order( option, quantity, message, - orderer + member ); } diff --git a/src/main/java/gift/product/model/Category.java b/src/main/java/gift/product/model/Category.java index be749bb22..90d79e2a8 100644 --- a/src/main/java/gift/product/model/Category.java +++ b/src/main/java/gift/product/model/Category.java @@ -12,17 +12,32 @@ public class Category { @Column(nullable = false, unique = true) private String name; + @Column(nullable = false) + private String color; + + @Column(nullable = false) + private String imageUrl; + + @Column(nullable = false) + private String description; + public Category() { } - public Category(String name) { + public Category(String name, String color, String imageUrl, String description) { this.name = name; + this.color = color; + this.imageUrl = imageUrl; + this.description = description; } - public Category(Long id, String name) { + public Category(Long id, String name, String color, String imageUrl, String description) { this.id = id; this.name = name; + this.color = color; + this.imageUrl = imageUrl; + this.description = description; } public Long getId() { @@ -32,4 +47,16 @@ public Long getId() { public String getName() { return name; } + + public String getColor() { + return color; + } + + public String getImageUrl() { + return imageUrl; + } + + public String getDescription() { + return description; + } } diff --git a/src/main/java/gift/product/model/Option.java b/src/main/java/gift/product/model/Option.java index 60717b454..ee7e9e035 100644 --- a/src/main/java/gift/product/model/Option.java +++ b/src/main/java/gift/product/model/Option.java @@ -7,6 +7,7 @@ import jakarta.persistence.*; import jakarta.validation.constraints.Pattern; import jakarta.validation.constraints.PositiveOrZero; +import java.util.Objects; @Entity public class Option { @@ -61,6 +62,8 @@ public Product getProduct() { } public boolean isSameName(Option option) { + if(Objects.equals(option.id, this.id)) + return false; return this.name.equals(option.getName()); } diff --git a/src/main/java/gift/product/model/Order.java b/src/main/java/gift/product/model/Order.java index 7b36f2e17..7f6051e34 100644 --- a/src/main/java/gift/product/model/Order.java +++ b/src/main/java/gift/product/model/Order.java @@ -7,10 +7,12 @@ import jakarta.persistence.Id; import jakarta.persistence.JoinColumn; import jakarta.persistence.ManyToOne; +import jakarta.persistence.Table; import jakarta.validation.constraints.Positive; import java.time.LocalDateTime; import java.time.format.DateTimeFormatter; +@Table(name = "orders") @Entity public class Order { @@ -34,18 +36,18 @@ public class Order { @ManyToOne @JoinColumn(nullable = false) - private Member orderer; + private Member member; public Order() { } - public Order(Option option, int quantity, String message, Member orderer) { + public Order(Option option, int quantity, String message, Member member) { this.option = option; this.quantity = quantity; this.orderDateTime = LocalDateTime.now(); this.message = message; - this.orderer = orderer; + this.member = member; } public Long getId() { @@ -68,8 +70,8 @@ public String getOrderDateTime() { public String getMessage() { return message; } - - public Member getOrderer() { - return orderer; + + public Member getMember() { + return member; } } diff --git a/src/main/java/gift/product/repository/OrderRepository.java b/src/main/java/gift/product/repository/OrderRepository.java index b97a6da58..9d798e0e1 100644 --- a/src/main/java/gift/product/repository/OrderRepository.java +++ b/src/main/java/gift/product/repository/OrderRepository.java @@ -1,8 +1,11 @@ package gift.product.repository; +import gift.product.model.Member; import gift.product.model.Order; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; import org.springframework.data.jpa.repository.JpaRepository; public interface OrderRepository extends JpaRepository { - + Page findAllByMember(Pageable pageable, Member member); } diff --git a/src/main/java/gift/product/service/OrderService.java b/src/main/java/gift/product/service/OrderService.java index 7af863676..2c971c761 100644 --- a/src/main/java/gift/product/service/OrderService.java +++ b/src/main/java/gift/product/service/OrderService.java @@ -21,8 +21,13 @@ import gift.product.util.JwtUtil; import jakarta.validation.constraints.NotNull; import java.net.URI; +import java.util.List; import java.util.Map; +import java.util.stream.Collectors; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.PageImpl; +import org.springframework.data.domain.Pageable; import org.springframework.http.HttpStatusCode; import org.springframework.http.MediaType; import org.springframework.stereotype.Service; @@ -80,7 +85,7 @@ public void sendToMe(Order order) { + "\n옵션 명: " + order.getOption().getName() + "\n수량: " + order.getQuantity() + "\n메세지: " + order.getMessage()); - String kakaoAccessToken = order.getOrderer().getSnsMember().getAccessToken(); + String kakaoAccessToken = order.getMember().getSnsMember().getAccessToken(); postRequest(kakaoAccessToken, body); } @@ -114,4 +119,25 @@ private void postRequest( .retrieve() .toEntity(String.class); } + + public Page getAllOrders(String authorization, Pageable pageable) { + Member member = jwtUtil.parsingToken(authorization); + Page myOrders = orderRepository.findAllByMember(pageable, member); + return convertToResponseDTOList(myOrders, pageable); + } + + private Page convertToResponseDTOList(Page myOrders, Pageable pageable) { + List orders = myOrders.stream() + .map(this::convertToResponseDTO) + .collect(Collectors.toList()); + return new PageImpl<>( + orders, + pageable, + myOrders.getTotalElements() + ); + } + + private OrderResponseDTO convertToResponseDTO(Order order) { + return new OrderResponseDTO(order); + } } diff --git a/src/main/java/gift/product/validation/OptionValidation.java b/src/main/java/gift/product/validation/OptionValidation.java index 5f9a3927c..4d8b7f7b3 100644 --- a/src/main/java/gift/product/validation/OptionValidation.java +++ b/src/main/java/gift/product/validation/OptionValidation.java @@ -58,7 +58,7 @@ private void validateOver100Million(int size) { private void validateDuplicateName(Collection options, Option newOption) { if(options.stream() - .anyMatch(option -> newOption.isSameName(option))) + .anyMatch(newOption::isSameName)) throw new DuplicateException(DUPLICATE_OPTION_NAME); } diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties index 17756a001..f084c7a0e 100644 --- a/src/main/resources/application.properties +++ b/src/main/resources/application.properties @@ -14,6 +14,9 @@ spring.jpa.hibernate.ddl-auto=create logging.level.org.hibernate.SQL=DEBUG logging.level.org.hibernate.orm.jdbc.bind=TRACE logging.level.org.hibernate.type.descriptor.sql.BasicBinder=TRACE +logging.level.org.springframework.web=DEBUG +logging.level.org.springframework.test=DEBUG +logging.level.gift=DEBUG # hidden-filter able spring.mvc.hiddenmethod.filter.enabled=true @@ -22,4 +25,4 @@ spring.mvc.hiddenmethod.filter.enabled=true spring.autoconfigure.exclude=org.springframework.boot.autoconfigure.security.servlet.SecurityAutoConfiguration kakao.client-id=d8e855663ae6bf0fba3c1493efa9086e -kakao.redirect-url=http://localhost:8080 \ No newline at end of file +kakao.redirect-url=http://3.35.51.40:8080/kakao/callback \ No newline at end of file diff --git a/src/main/resources/templates/category-form.html b/src/main/resources/templates/category-form.html index f90a2d2c4..ab69881ba 100644 --- a/src/main/resources/templates/category-form.html +++ b/src/main/resources/templates/category-form.html @@ -5,21 +5,42 @@ 카테고리 추가 페이지 -<- 카테고리 관리 페이지로 돌아가기 +<- 카테고리 관리 페이지로 돌아가기 카테고리 추가 - - - 카테고리 명: - - - - - - - 추가 - - - + + + + 카테고리 명: + + + + Category Name Error Message + + + 색상: + + + + Category Color Error Message + + + 이미지 주소: + + + + Category ImageUrl Error Message + + + 설명: + + + + Category Description Error Message + + + 카테고리 추가 + +