From 34ba8112872de856e8f024a180ac6a1be9d04bfb Mon Sep 17 00:00:00 2001 From: seolbin96 Date: Wed, 24 Jul 2024 15:24:57 +0900 Subject: [PATCH 01/13] =?UTF-8?q?feat=20:=20=EC=9D=B4=EC=A0=84=20=EA=B3=BC?= =?UTF-8?q?=EC=A0=9C=20=EC=BD=94=EB=93=9C=20=EA=B0=80=EC=A0=B8=EC=98=A4?= =?UTF-8?q?=EA=B8=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- build.gradle | 15 +- src/main/java/gift/config/WebConfig.java | 23 +++ .../gift/controller/CategoryController.java | 42 +++++ .../gift/controller/OptionController.java | 31 ++++ .../controller/ProductAdminController.java | 57 +++++++ .../gift/controller/ProductController.java | 58 +++++++ .../java/gift/controller/UserController.java | 45 +++++ .../gift/controller/WishlistController.java | 43 +++++ src/main/java/gift/dto/CategoryDTO.java | 61 +++++++ src/main/java/gift/dto/OptionDTO.java | 41 +++++ src/main/java/gift/dto/ProductDTO.java | 77 +++++++++ src/main/java/gift/dto/TokenResponse.java | 17 ++ src/main/java/gift/dto/WishlistDTO.java | 19 +++ .../exception/GlobalExceptionHandler.java | 45 +++++ src/main/java/gift/model/Category.java | 76 +++++++++ src/main/java/gift/model/Option.java | 75 +++++++++ src/main/java/gift/model/Product.java | 93 +++++++++++ src/main/java/gift/model/User.java | 60 +++++++ src/main/java/gift/model/Wishlist.java | 50 ++++++ .../gift/repository/CategoryRepository.java | 10 ++ .../gift/repository/OptionRepository.java | 12 ++ .../gift/repository/ProductRepository.java | 12 ++ .../java/gift/repository/UserRepository.java | 10 ++ .../gift/repository/WishlistRepository.java | 14 ++ .../java/gift/security/JwtTokenProvider.java | 28 ++++ src/main/java/gift/security/LoginMember.java | 11 ++ .../security/LoginMemberArgumentResolver.java | 50 ++++++ .../java/gift/service/CategoryService.java | 44 +++++ src/main/java/gift/service/OptionService.java | 45 +++++ .../java/gift/service/ProductService.java | 109 ++++++++++++ src/main/java/gift/service/UserService.java | 40 +++++ .../java/gift/service/WishlistService.java | 57 +++++++ src/main/resources/application.properties | 18 ++ src/main/resources/data.sql | 44 +++++ src/main/resources/schema.sql | 26 +++ src/main/resources/static/css/styles.css | 91 ++++++++++ src/main/resources/templates/productForm.html | 27 +++ src/main/resources/templates/productList.html | 50 ++++++ .../java/gift/service/OptionServiceTest.java | 68 ++++++++ .../resources/http-requests/requests.http | 158 ++++++++++++++++++ 40 files changed, 1850 insertions(+), 2 deletions(-) create mode 100644 src/main/java/gift/config/WebConfig.java create mode 100644 src/main/java/gift/controller/CategoryController.java create mode 100644 src/main/java/gift/controller/OptionController.java create mode 100644 src/main/java/gift/controller/ProductAdminController.java create mode 100644 src/main/java/gift/controller/ProductController.java create mode 100644 src/main/java/gift/controller/UserController.java create mode 100644 src/main/java/gift/controller/WishlistController.java create mode 100644 src/main/java/gift/dto/CategoryDTO.java create mode 100644 src/main/java/gift/dto/OptionDTO.java create mode 100644 src/main/java/gift/dto/ProductDTO.java create mode 100644 src/main/java/gift/dto/TokenResponse.java create mode 100644 src/main/java/gift/dto/WishlistDTO.java create mode 100644 src/main/java/gift/exception/GlobalExceptionHandler.java create mode 100644 src/main/java/gift/model/Category.java create mode 100644 src/main/java/gift/model/Option.java create mode 100644 src/main/java/gift/model/Product.java create mode 100644 src/main/java/gift/model/User.java create mode 100644 src/main/java/gift/model/Wishlist.java create mode 100644 src/main/java/gift/repository/CategoryRepository.java create mode 100644 src/main/java/gift/repository/OptionRepository.java create mode 100644 src/main/java/gift/repository/ProductRepository.java create mode 100644 src/main/java/gift/repository/UserRepository.java create mode 100644 src/main/java/gift/repository/WishlistRepository.java create mode 100644 src/main/java/gift/security/JwtTokenProvider.java create mode 100644 src/main/java/gift/security/LoginMember.java create mode 100644 src/main/java/gift/security/LoginMemberArgumentResolver.java create mode 100644 src/main/java/gift/service/CategoryService.java create mode 100644 src/main/java/gift/service/OptionService.java create mode 100644 src/main/java/gift/service/ProductService.java create mode 100644 src/main/java/gift/service/UserService.java create mode 100644 src/main/java/gift/service/WishlistService.java create mode 100644 src/main/resources/data.sql create mode 100644 src/main/resources/schema.sql create mode 100644 src/main/resources/static/css/styles.css create mode 100644 src/main/resources/templates/productForm.html create mode 100644 src/main/resources/templates/productList.html create mode 100644 src/test/java/gift/service/OptionServiceTest.java create mode 100644 src/test/resources/http-requests/requests.http diff --git a/build.gradle b/build.gradle index df7db9334..4d47e8fea 100644 --- a/build.gradle +++ b/build.gradle @@ -18,14 +18,25 @@ repositories { } dependencies { + runtimeOnly 'com.h2database:h2' implementation 'org.springframework.boot:spring-boot-starter-jdbc' implementation 'org.springframework.boot:spring-boot-starter-thymeleaf' implementation 'org.springframework.boot:spring-boot-starter-web' - runtimeOnly 'com.h2database:h2' testImplementation 'org.springframework.boot:spring-boot-starter-test' testRuntimeOnly 'org.junit.platform:junit-platform-launcher' + implementation 'org.springframework.boot:spring-boot-starter-validation' + + compileOnly 'io.jsonwebtoken:jjwt-api:0.12.6' + runtimeOnly 'io.jsonwebtoken:jjwt-impl:0.12.6' + runtimeOnly 'io.jsonwebtoken:jjwt-jackson:0.12.6' + implementation 'org.springframework.boot:spring-boot-starter-data-jpa' + + testImplementation 'org.junit.jupiter:junit-jupiter-api:5.7.1' + testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.7.1' + testImplementation 'org.mockito:mockito-core:3.6.28' + testImplementation 'org.mockito:mockito-junit-jupiter:3.6.28' } tasks.named('test') { useJUnitPlatform() -} +} \ No newline at end of file diff --git a/src/main/java/gift/config/WebConfig.java b/src/main/java/gift/config/WebConfig.java new file mode 100644 index 000000000..bd69daf56 --- /dev/null +++ b/src/main/java/gift/config/WebConfig.java @@ -0,0 +1,23 @@ +package gift.config; + +import gift.security.LoginMemberArgumentResolver; +import org.springframework.context.annotation.Configuration; +import org.springframework.web.method.support.HandlerMethodArgumentResolver; +import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; + +import java.util.List; + +@Configuration +public class WebConfig implements WebMvcConfigurer { + + private final LoginMemberArgumentResolver loginMemberArgumentResolver; + + public WebConfig(LoginMemberArgumentResolver loginMemberArgumentResolver) { + this.loginMemberArgumentResolver = loginMemberArgumentResolver; + } + + @Override + public void addArgumentResolvers(List resolvers) { + resolvers.add(loginMemberArgumentResolver); + } +} \ No newline at end of file diff --git a/src/main/java/gift/controller/CategoryController.java b/src/main/java/gift/controller/CategoryController.java new file mode 100644 index 000000000..12e34af8f --- /dev/null +++ b/src/main/java/gift/controller/CategoryController.java @@ -0,0 +1,42 @@ +package gift.controller; + +import gift.dto.CategoryDTO; +import gift.service.CategoryService; +import org.springframework.data.domain.Page; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.*; + +@RestController +@RequestMapping("/api/categories") +public class CategoryController { + private final CategoryService categoryService; + + public CategoryController(CategoryService categoryService) { + this.categoryService = categoryService; + } + + @GetMapping + public ResponseEntity> readCategory(@RequestParam int page, @RequestParam int size) { + Page categories = categoryService.findAll(page, size); + return new ResponseEntity<>(categories, HttpStatus.OK); + } + + @PostMapping + public ResponseEntity createCategory(@RequestBody CategoryDTO categoryDTO) { + categoryService.save(categoryDTO); + return new ResponseEntity<>(HttpStatus.CREATED); + } + + @PutMapping("/{category_id}") + public ResponseEntity updateCategory(@PathVariable("category_id") Long id, @RequestBody CategoryDTO categoryDTO) { + categoryService.update(id, categoryDTO); + return new ResponseEntity<>(HttpStatus.OK); + } + + @DeleteMapping("/{category_id}") + public ResponseEntity deleteCategory(@PathVariable("category_id") Long id) { + categoryService.delete(id); + return new ResponseEntity<>(HttpStatus.NO_CONTENT); + } +} \ No newline at end of file diff --git a/src/main/java/gift/controller/OptionController.java b/src/main/java/gift/controller/OptionController.java new file mode 100644 index 000000000..338220b30 --- /dev/null +++ b/src/main/java/gift/controller/OptionController.java @@ -0,0 +1,31 @@ +package gift.controller; + +import gift.dto.OptionDTO; +import gift.service.OptionService; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.*; + +import java.util.List; + +@RestController +@RequestMapping("/api/products/{productId}/options") +public class OptionController { + private final OptionService optionService; + + public OptionController(OptionService optionService) { + this.optionService = optionService; + } + + @GetMapping + public ResponseEntity> getOptionsByProductId(@PathVariable Long productId) { + List options = optionService.getOptionsByProductId(productId); + return new ResponseEntity<>(options, HttpStatus.OK); + } + + @PostMapping + public ResponseEntity addOptionToProduct(@PathVariable Long productId, @RequestBody OptionDTO optionDTO) { + OptionDTO createdOption = optionService.addOptionToProduct(productId, optionDTO); + return new ResponseEntity<>(createdOption, HttpStatus.CREATED); + } +} \ No newline at end of file diff --git a/src/main/java/gift/controller/ProductAdminController.java b/src/main/java/gift/controller/ProductAdminController.java new file mode 100644 index 000000000..d6bb23012 --- /dev/null +++ b/src/main/java/gift/controller/ProductAdminController.java @@ -0,0 +1,57 @@ +package gift.controller; + +import gift.dto.ProductDTO; +import gift.service.ProductService; +import org.springframework.data.domain.Page; +import org.springframework.stereotype.Controller; +import org.springframework.ui.Model; +import org.springframework.web.bind.annotation.*; + +@Controller +@RequestMapping("/admin/products") +public class ProductAdminController { + + private final ProductService productService; + + public ProductAdminController(ProductService productService) { + this.productService = productService; + } + + @GetMapping + public String listProducts(Model model, @RequestParam(defaultValue = "0") int page, @RequestParam(defaultValue = "5") int size) { + Page productsPage = productService.getAllProducts(page, size); + model.addAttribute("productsPage", productsPage); + return "productList"; + } + + @GetMapping("/add") + public String addProductForm(Model model) { + model.addAttribute("product", new ProductDTO()); + return "productForm"; + } + + @PostMapping("/add") + public String addProduct(@ModelAttribute ProductDTO productDTO) { + productService.saveProduct(productDTO); + return "redirect:/admin/products"; + } + + @GetMapping("/edit/{id}") + public String editProductForm(@PathVariable Long id, Model model) { + ProductDTO productDTO = productService.getProductById(id); + model.addAttribute("product", productDTO); + return "productForm"; + } + + @PostMapping("/edit/{id}") + public String updateProduct(@PathVariable Long id, @ModelAttribute ProductDTO productDTO) { + productService.updateProduct(id, productDTO); + return "redirect:/admin/products"; + } + + @GetMapping("/delete/{id}") + public String deleteProduct(@PathVariable Long id) { + productService.deleteProduct(id); + return "redirect:/admin/products"; + } +} \ No newline at end of file diff --git a/src/main/java/gift/controller/ProductController.java b/src/main/java/gift/controller/ProductController.java new file mode 100644 index 000000000..7b8d2f96a --- /dev/null +++ b/src/main/java/gift/controller/ProductController.java @@ -0,0 +1,58 @@ +package gift.controller; + +import gift.dto.ProductDTO; +import gift.service.ProductService; +import org.springframework.data.domain.Page; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.validation.BindingResult; +import org.springframework.web.bind.annotation.*; + +import jakarta.validation.Valid; + +@RestController +@RequestMapping("/api/products") +public class ProductController { + + private final ProductService productService; + + public ProductController(ProductService productService) { + this.productService = productService; + } + + @GetMapping + public ResponseEntity> getAllProducts(@RequestParam int page, @RequestParam int size) { + Page products = productService.getAllProducts(page, size); + return new ResponseEntity<>(products, HttpStatus.OK); + } + + @GetMapping("/{id}") + public ResponseEntity getProductById(@PathVariable Long id) { + ProductDTO product = productService.getProductById(id); + return new ResponseEntity<>(product, HttpStatus.OK); + } + + @PostMapping + public ResponseEntity addProduct(@RequestBody @Valid ProductDTO productDTO, BindingResult bindingResult) { + if (bindingResult.hasErrors()) { + return new ResponseEntity<>(HttpStatus.BAD_REQUEST); + } + productService.saveProduct(productDTO); + return new ResponseEntity<>(HttpStatus.CREATED); + } + + @PutMapping("/{id}") + public ResponseEntity updateProduct(@PathVariable Long id, @RequestBody @Valid ProductDTO productDTO, BindingResult bindingResult) { + if (bindingResult.hasErrors()) { + return new ResponseEntity<>(HttpStatus.BAD_REQUEST); + } + productService.updateProduct(id, productDTO); + return new ResponseEntity<>(HttpStatus.NO_CONTENT); + } + + @DeleteMapping("/{id}") + public ResponseEntity deleteProduct(@PathVariable Long id) { + productService.deleteProduct(id); + return new ResponseEntity<>(HttpStatus.NO_CONTENT); + } +} \ No newline at end of file diff --git a/src/main/java/gift/controller/UserController.java b/src/main/java/gift/controller/UserController.java new file mode 100644 index 000000000..4fa9dbec8 --- /dev/null +++ b/src/main/java/gift/controller/UserController.java @@ -0,0 +1,45 @@ +package gift.controller; + +import gift.model.User; +import gift.service.UserService; +import gift.dto.TokenResponse; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.*; + +@RestController +@RequestMapping("/members") +public class UserController { + private final UserService userService; + + @Autowired + public UserController(UserService userService) { + this.userService = userService; + } + + @PostMapping("/register") + public ResponseEntity register(@RequestBody User user) { + try { + userService.createUser(user); + TokenResponse tokenResponse = userService.createTokenResponse(user); + return new ResponseEntity<>(tokenResponse, HttpStatus.CREATED); + } catch (Exception e) { + return new ResponseEntity<>(null, HttpStatus.INTERNAL_SERVER_ERROR); + } + } + + @PostMapping("/login") + public ResponseEntity login(@RequestBody User user) { + try { + User savedUser = userService.loadOneUser(user.getEmail()); + if (savedUser != null && savedUser.getPassword().equals(user.getPassword())) { + TokenResponse tokenResponse = userService.createTokenResponse(savedUser); + return new ResponseEntity<>(tokenResponse, HttpStatus.OK); + } + return new ResponseEntity<>(null, HttpStatus.UNAUTHORIZED); + } catch (Exception e) { + return new ResponseEntity<>(null, HttpStatus.INTERNAL_SERVER_ERROR); + } + } +} \ No newline at end of file diff --git a/src/main/java/gift/controller/WishlistController.java b/src/main/java/gift/controller/WishlistController.java new file mode 100644 index 000000000..5fbf677a1 --- /dev/null +++ b/src/main/java/gift/controller/WishlistController.java @@ -0,0 +1,43 @@ +package gift.controller; + +import gift.dto.ProductDTO; +import gift.dto.WishlistDTO; +import gift.model.User; +import gift.security.LoginMember; +import gift.service.WishlistService; +import org.springframework.data.domain.Page; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.*; + +@RestController +@RequestMapping("/api/wishlist") +public class WishlistController { + + private final WishlistService wishlistService; + + public WishlistController(WishlistService wishlistService) { + this.wishlistService = wishlistService; + } + + @PostMapping("/{productId}") + public ResponseEntity addWishlist(@PathVariable("productId") Long id, @LoginMember User user) { + WishlistDTO wishlistDTO = new WishlistDTO(user.getEmail(), id); + wishlistService.addWishlist(wishlistDTO); + return new ResponseEntity<>(HttpStatus.OK); + } + + @GetMapping + public ResponseEntity> getWishlist(@LoginMember User user, + @RequestParam(defaultValue = "0") int page, + @RequestParam(defaultValue = "5") int size) { + Page products = wishlistService.getProductsFromWishlist(user.getEmail(), page, size); + return new ResponseEntity<>(products, HttpStatus.OK); + } + + @DeleteMapping("/{productId}") + public ResponseEntity deleteWishlist(@PathVariable("productId") Long id, @LoginMember User user) { + wishlistService.deleteWishlist(user.getEmail(), id); + return new ResponseEntity<>(HttpStatus.OK); + } +} \ No newline at end of file diff --git a/src/main/java/gift/dto/CategoryDTO.java b/src/main/java/gift/dto/CategoryDTO.java new file mode 100644 index 000000000..5d0beb83d --- /dev/null +++ b/src/main/java/gift/dto/CategoryDTO.java @@ -0,0 +1,61 @@ +package gift.dto; + +import gift.model.Category; + +public class CategoryDTO { + private Long id; + private String name; + private String color; + private String imageUrl; + private String description; + + public CategoryDTO() {} + + public CategoryDTO(Category category) { + this.id = category.getId(); + this.name = category.getName(); + this.color = category.getColor(); + this.imageUrl = category.getImageUrl(); + this.description = category.getDescription(); + } + + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getColor() { + return color; + } + + public void setColor(String color) { + this.color = color; + } + + public String getImageUrl() { + return imageUrl; + } + + public void setImageUrl(String imageUrl) { + this.imageUrl = imageUrl; + } + + public String getDescription() { + return description; + } + + public void setDescription(String description) { + this.description = description; + } +} \ No newline at end of file diff --git a/src/main/java/gift/dto/OptionDTO.java b/src/main/java/gift/dto/OptionDTO.java new file mode 100644 index 000000000..1815d38b4 --- /dev/null +++ b/src/main/java/gift/dto/OptionDTO.java @@ -0,0 +1,41 @@ +package gift.dto; + +import gift.model.Option; + +public class OptionDTO { + private Long id; + private String name; + private int quantity; + + public OptionDTO() {} + + public OptionDTO(Option option) { + this.id = option.getId(); + this.name = option.getName(); + this.quantity = option.getQuantity(); + } + + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public int getQuantity() { + return quantity; + } + + public void setQuantity(int quantity) { + this.quantity = quantity; + } +} \ No newline at end of file diff --git a/src/main/java/gift/dto/ProductDTO.java b/src/main/java/gift/dto/ProductDTO.java new file mode 100644 index 000000000..54660f14a --- /dev/null +++ b/src/main/java/gift/dto/ProductDTO.java @@ -0,0 +1,77 @@ +package gift.dto; + +import gift.model.Product; +import java.util.Set; +import java.util.stream.Collectors; + +public class ProductDTO { + private Long id; + private String name; + private int price; + private String imageUrl; + private CategoryDTO category; + private Set options; + + public ProductDTO() {} + + public ProductDTO(Product product) { + this.id = product.getId(); + this.name = product.getName(); + this.price = product.getPrice(); + this.imageUrl = product.getImageUrl(); + if (product.getCategory() != null) { + this.category = new CategoryDTO(product.getCategory()); + } + if (product.getOptions() != null) { + this.options = product.getOptions().stream().map(OptionDTO::new).collect(Collectors.toSet()); + } + } + + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public int getPrice() { + return price; + } + + public void setPrice(int price) { + this.price = price; + } + + public String getImageUrl() { + return imageUrl; + } + + public void setImageUrl(String imageUrl) { + this.imageUrl = imageUrl; + } + + public CategoryDTO getCategory() { + return category; + } + + public void setCategory(CategoryDTO category) { + this.category = category; + } + + public Set getOptions() { + return options; + } + + public void setOptions(Set options) { + this.options = options; + } +} \ No newline at end of file diff --git a/src/main/java/gift/dto/TokenResponse.java b/src/main/java/gift/dto/TokenResponse.java new file mode 100644 index 000000000..3c0179579 --- /dev/null +++ b/src/main/java/gift/dto/TokenResponse.java @@ -0,0 +1,17 @@ +package gift.dto; + +public class TokenResponse { + private String token; + + public TokenResponse(String token) { + this.token = token; + } + + public String getToken() { + return token; + } + + public void setToken(String token) { + this.token = token; + } +} \ No newline at end of file diff --git a/src/main/java/gift/dto/WishlistDTO.java b/src/main/java/gift/dto/WishlistDTO.java new file mode 100644 index 000000000..3e4cbd187 --- /dev/null +++ b/src/main/java/gift/dto/WishlistDTO.java @@ -0,0 +1,19 @@ +package gift.dto; + +public class WishlistDTO { + private String userId; + private Long productId; + + public WishlistDTO(String userId, Long productId) { + this.userId = userId; + this.productId = productId; + } + + public String getUserId() { + return userId; + } + + public Long getProductId() { + return productId; + } +} \ No newline at end of file diff --git a/src/main/java/gift/exception/GlobalExceptionHandler.java b/src/main/java/gift/exception/GlobalExceptionHandler.java new file mode 100644 index 000000000..e0b850e9f --- /dev/null +++ b/src/main/java/gift/exception/GlobalExceptionHandler.java @@ -0,0 +1,45 @@ +package gift.exception; + +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.MethodArgumentNotValidException; +import org.springframework.web.bind.annotation.ControllerAdvice; +import org.springframework.web.bind.annotation.ExceptionHandler; +import org.springframework.web.bind.annotation.ResponseStatus; + +import java.time.LocalDateTime; +import java.util.HashMap; +import java.util.Map; + +@ControllerAdvice +public class GlobalExceptionHandler { + + @ExceptionHandler(MethodArgumentNotValidException.class) + @ResponseStatus(HttpStatus.BAD_REQUEST) + public ResponseEntity> handleValidationExceptions(MethodArgumentNotValidException ex) { + Map errors = new HashMap<>(); + ex.getBindingResult().getFieldErrors().forEach(error -> + errors.put(error.getField(), error.getDefaultMessage())); + return new ResponseEntity<>(errors, HttpStatus.BAD_REQUEST); + } + + @ExceptionHandler(IllegalArgumentException.class) + @ResponseStatus(HttpStatus.BAD_REQUEST) + public ResponseEntity handleIllegalArgumentException(IllegalArgumentException ex) { + return new ResponseEntity<>(ex.getMessage(), HttpStatus.BAD_REQUEST); + } + + @ExceptionHandler(Exception.class) + public ResponseEntity> handleAllExceptions(Exception ex) { + Map body = new HashMap<>(); + body.put("timestamp", LocalDateTime.now()); + body.put("status", HttpStatus.INTERNAL_SERVER_ERROR.value()); + body.put("error", "Internal Server Error"); + body.put("message", ex.getMessage()); + body.put("path", ""); + + ex.printStackTrace(); + + return new ResponseEntity<>(body, HttpStatus.INTERNAL_SERVER_ERROR); + } +} \ No newline at end of file diff --git a/src/main/java/gift/model/Category.java b/src/main/java/gift/model/Category.java new file mode 100644 index 000000000..178fe0491 --- /dev/null +++ b/src/main/java/gift/model/Category.java @@ -0,0 +1,76 @@ +package gift.model; + +import gift.dto.CategoryDTO; +import jakarta.persistence.*; + +@Entity +@Table(name = "categories") +public class Category { + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + + @Column(nullable = false, unique = true) + private String name; + + @Column(nullable = false) + private String color; + + @Column(nullable = false, name = "image_url") + private String imageUrl; + + @Column + private String description; + + public Category() {} + + public Category(String name, String color, String imageUrl, String description) { + this.name = name; + this.color = color; + this.imageUrl = imageUrl; + this.description = description; + } + + public Long getId() { + return id; + } + + public String getName() { + return name; + } + + public String getColor() { + return color; + } + + public String getImageUrl() { + return imageUrl; + } + + public String getDescription() { + return description; + } + + public void setName(String name) { + this.name = name; + } + + public void setColor(String color) { + this.color = color; + } + + public void setImageUrl(String imageUrl) { + this.imageUrl = imageUrl; + } + + public void setDescription(String description) { + this.description = description; + } + + public void updateFromDTO(CategoryDTO categoryDTO) { + this.name = categoryDTO.getName(); + this.color = categoryDTO.getColor(); + this.imageUrl = categoryDTO.getImageUrl(); + this.description = categoryDTO.getDescription(); + } +} \ No newline at end of file diff --git a/src/main/java/gift/model/Option.java b/src/main/java/gift/model/Option.java new file mode 100644 index 000000000..686744d8a --- /dev/null +++ b/src/main/java/gift/model/Option.java @@ -0,0 +1,75 @@ +package gift.model; + +import jakarta.persistence.*; +import jakarta.validation.constraints.*; + +@Entity +@Table(name = "options") +public class Option { + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + + @NotBlank(message = "옵션 이름을 입력해주세요.") + @Size(max = 50, message = "옵션 이름은 50자 이내여야 합니다.") + @Pattern(regexp = "^[ㄱ-ㅎ가-힣a-zA-Z0-9\\s\\(\\)\\[\\]+&\\-/_]*$", message = "옵션 이름에 유효하지 않은 문자가 포함되어 있습니다.") + @Column(nullable = false) + private String name; + + @Min(value = 1, message = "옵션 수량은 1개 이상이어야 합니다.") + @Max(value = 99999999, message = "옵션 수량은 1억 개 미만이어야 합니다.") + @Column(nullable = false) + private int quantity; + + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "product_id", nullable = false) + private Product product; + + protected Option() {} + + public Option(Long id, String name, int quantity, Product product) { + this.id = id; + this.name = name; + this.quantity = quantity; + this.product = product; + } + + public Long getId() { + return id; + } + + public String getName() { + return name; + } + + public int getQuantity() { + return quantity; + } + + public Product getProduct() { + return product; + } + + public void setId(Long id) { + this.id = id; + } + + public void setName(String name) { + this.name = name; + } + + public void setQuantity(int quantity) { + this.quantity = quantity; + } + + public void setProduct(Product product) { + this.product = product; + } + + public void subtractQuantity(int quantity) { + if (this.quantity < quantity) { + throw new IllegalArgumentException("Option quantity cannot be less than 0."); + } + this.quantity -= quantity; + } +} \ No newline at end of file diff --git a/src/main/java/gift/model/Product.java b/src/main/java/gift/model/Product.java new file mode 100644 index 000000000..6ef11ba9b --- /dev/null +++ b/src/main/java/gift/model/Product.java @@ -0,0 +1,93 @@ +package gift.model; + +import jakarta.persistence.*; +import jakarta.validation.constraints.*; + +import java.util.HashSet; +import java.util.Set; + +@Entity +@Table(name = "products") +public class Product { + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + + @NotBlank(message = "상품 이름을 입력해주세요.") + @Size(max = 255, message = "상품 이름은 255자 이내여야 합니다.") + @Pattern(regexp = "^[ㄱ-ㅎ가-힣a-zA-Z0-9\\s\\(\\)\\[\\]+&\\-/_]*$", message = "상품 이름에 유효하지 않은 문자가 포함되어 있습니다.") + @Column(nullable = false) + private String name; + + @Min(value = 0, message = "상품 가격은 0원 이상이어야 합니다.") + @Column(nullable = false) + private int price; + + @Column(nullable = false, name = "image_url") + private String imageUrl; + + @ManyToOne(fetch = FetchType.EAGER) + @JoinColumn(name = "category_id", nullable = false) + private Category category; + + @OneToMany(mappedBy = "product", cascade = CascadeType.ALL, orphanRemoval = true) + private Set