-
Notifications
You must be signed in to change notification settings - Fork 0
Feat/#4 장바구니 조회, 제품 등록, 수량 수정, 삭제 api #9
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
cc6e999
ff32d39
dee4601
e44b583
dbdc92c
bd2c4d2
223d837
ae77b0a
616cef3
646a43f
e88d56a
4209b8b
16a3b60
219ab60
8cd60ed
37bf7e9
4c8a5e8
694472f
40307f2
25be277
00ccf14
a252a45
7dc4602
b683141
52aaa9d
6c90318
47a33fa
adc5ce3
470430a
4773fbd
e882f8c
931ef59
c37fd59
a79fa91
1fe35c0
0731ad3
4ee9377
9ba5b63
8188cda
26c8ef0
51f44cd
4e82b4e
c7fb43e
82b7c77
0f5e076
34d28ce
d4740ac
2cb340a
a306d93
27ddd42
32a0e8c
0c1e9a0
4ad7f86
3b2445c
86a355b
9c61f64
f92aa81
8862ee1
25da040
de8a3b1
49b6403
48b8a63
6de61cd
746d6f2
db35984
c6bb1b0
cbd15f0
8f2f1b1
3f057f3
b8df186
5b2c07e
db57b3b
140ce59
1c86edb
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,47 @@ | ||
package com.example.api.module.cart.controller; | ||
|
||
import com.example.api.module.cart.controller.request.CartProductAddRequest; | ||
import com.example.api.module.cart.controller.request.CartProductQuantityUpdateRequest; | ||
import com.example.api.module.cart.controller.response.CartSummaryResponse; | ||
import com.example.api.module.cart.service.CartService; | ||
import com.example.core.model.response.DataResponse; | ||
import lombok.RequiredArgsConstructor; | ||
import org.springframework.security.core.annotation.AuthenticationPrincipal; | ||
import org.springframework.web.bind.annotation.*; | ||
|
||
@RestController | ||
@RequiredArgsConstructor | ||
@RequestMapping("/api/v1/cart") | ||
public class CartController { | ||
|
||
private final CartService cartService; | ||
|
||
@GetMapping() | ||
public DataResponse<CartSummaryResponse> getCart( | ||
@AuthenticationPrincipal Long userId) { | ||
return DataResponse.of(cartService.getCart(userId)); | ||
} | ||
|
||
|
||
@PostMapping("/products") | ||
public DataResponse<CartSummaryResponse> addCartProduct( | ||
@AuthenticationPrincipal Long userId, | ||
@RequestBody CartProductAddRequest req) { | ||
return DataResponse.of(cartService.addCartProduct(userId, req)); | ||
} | ||
|
||
@PutMapping("/products/{productId}") | ||
public DataResponse<CartSummaryResponse> updateCartProduct( | ||
@AuthenticationPrincipal Long userId, | ||
@PathVariable Long productId, | ||
@RequestBody CartProductQuantityUpdateRequest req) { | ||
return DataResponse.of(cartService.updateCartProductQuantity(userId, productId, req)); | ||
} | ||
|
||
@DeleteMapping("/products/{productId}") | ||
public DataResponse<CartSummaryResponse> deleteCartProduct( | ||
@AuthenticationPrincipal Long userId, | ||
@PathVariable Long productId) { | ||
return DataResponse.of(cartService.deleteCartProduct(userId, productId)); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
package com.example.api.module.cart.controller.request; | ||
|
||
import lombok.AllArgsConstructor; | ||
import lombok.Data; | ||
|
||
@Data | ||
@AllArgsConstructor | ||
public class CartProductAddRequest { | ||
private Long productId; | ||
private Long addQuantity; | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
package com.example.api.module.cart.controller.request; | ||
|
||
import lombok.AllArgsConstructor; | ||
import lombok.Data; | ||
|
||
@Data | ||
@AllArgsConstructor | ||
public class CartProductQuantityUpdateRequest { | ||
private Long quantity; | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,29 @@ | ||
package com.example.api.module.cart.controller.response; | ||
|
||
import com.example.core.dto.CartProductDto; | ||
import lombok.AllArgsConstructor; | ||
import lombok.Builder; | ||
import lombok.Data; | ||
import lombok.NoArgsConstructor; | ||
|
||
import java.util.List; | ||
|
||
@Data | ||
@AllArgsConstructor | ||
@NoArgsConstructor | ||
@Builder | ||
public class CartSummaryResponse { | ||
List<CartProductDto> cartProductList; | ||
Long cartTotalPrice; //총 가격 | ||
|
||
public static CartSummaryResponse of(List<CartProductDto> cartProductList) { | ||
Long totalPrice = cartProductList.stream() | ||
.mapToLong(CartProductDto::getProductTotalPrice) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 이전에 말씀드린대로 BigDecimal 로 처리하시는게 좋습니다 |
||
.sum(); | ||
|
||
return builder() | ||
.cartProductList(cartProductList) | ||
.cartTotalPrice(totalPrice) | ||
.build(); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,145 @@ | ||
package com.example.api.module.cart.service; | ||
|
||
import com.example.api.module.cart.controller.request.CartProductAddRequest; | ||
import com.example.api.module.cart.controller.request.CartProductQuantityUpdateRequest; | ||
import com.example.api.module.cart.controller.response.CartSummaryResponse; | ||
import com.example.core.domain.cart.Cart; | ||
import com.example.core.domain.cart_product.CartProduct; | ||
import com.example.core.domain.cart_product.api.CartProductApiRepository; | ||
import com.example.core.domain.product.Product; | ||
import com.example.core.domain.product.api.ProductApiRepository; | ||
import com.example.core.domain.user.User; | ||
import com.example.core.domain.user.api.UserApiRepository; | ||
import com.example.core.dto.CartProductDto; | ||
import com.example.core.dto.ProductSummaryDto; | ||
import com.example.core.enums.CartProductStatus; | ||
import com.example.core.exception.BadRequestException; | ||
import lombok.AllArgsConstructor; | ||
import org.springframework.cache.annotation.CacheEvict; | ||
import org.springframework.cache.annotation.Cacheable; | ||
import org.springframework.stereotype.Service; | ||
import org.springframework.transaction.annotation.Transactional; | ||
|
||
import java.util.ArrayList; | ||
import java.util.List; | ||
import java.util.Optional; | ||
|
||
@Service | ||
@AllArgsConstructor | ||
public class CartService { | ||
|
||
private final UserApiRepository userApiRepository; | ||
private final ProductApiRepository productApiRepository; | ||
private final CartProductApiRepository cartProductApiRepository; | ||
|
||
@Cacheable(value = "cart", key = "#userId") | ||
@Transactional(readOnly = true) | ||
public CartSummaryResponse getCart(Long userId) { | ||
User user = userApiRepository.findById(userId) | ||
.orElseThrow(() -> new BadRequestException("User not found")); | ||
return getCartSummary(user); | ||
} | ||
|
||
@CacheEvict(value = "cart", key = "#userId") | ||
@Transactional | ||
public CartSummaryResponse addCartProduct(Long userId, CartProductAddRequest req) { | ||
User user = userApiRepository.findById(userId) | ||
.orElseThrow(() -> new BadRequestException("User not found")); | ||
Cart cart = user.getCart(); | ||
Product product = productApiRepository.findById(req.getProductId()) | ||
.orElseThrow(() -> new BadRequestException("Product not found")); | ||
|
||
Long quantity = req.getAddQuantity(); | ||
if (quantity <= 0) throw new BadRequestException("Quantity must be greater than 0"); | ||
|
||
Optional<CartProduct> cartProductOpt = cartProductApiRepository. | ||
findByCart_IdAndProduct_Id(cart.getId(), req.getProductId()); | ||
|
||
CartProduct cartProduct; | ||
if (cartProductOpt.isPresent()) { | ||
//update existing cart product | ||
cartProduct = cartProductOpt.get(); | ||
cartProduct.addQuantity(quantity); | ||
} else { | ||
//create new cart product | ||
cartProduct = CartProduct.of(cart, product, quantity); | ||
} | ||
cartProductApiRepository.save(cartProduct); | ||
return getCartSummary(user); | ||
} | ||
|
||
@CacheEvict(value = "cart", key = "#userId") | ||
@Transactional | ||
public CartSummaryResponse updateCartProductQuantity(Long userId, Long productId, CartProductQuantityUpdateRequest req) { | ||
User user = userApiRepository.findById(userId) | ||
.orElseThrow(() -> new BadRequestException("User not found")); | ||
Cart cart = user.getCart(); | ||
|
||
CartProduct cartProduct = cartProductApiRepository.findByCart_IdAndProduct_Id(cart.getId(), productId) | ||
.orElseThrow(() -> new BadRequestException("Cart product not found")); | ||
|
||
Long quantity = req.getQuantity(); | ||
|
||
if (quantity <= 0) { | ||
cartProductApiRepository.delete(cartProduct); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Softdelete 가 좋을 것 같습니다 |
||
} else { | ||
cartProduct.setQuantity(quantity); | ||
cartProductApiRepository.save(cartProduct); | ||
} | ||
return getCartSummary(user); | ||
} | ||
|
||
@CacheEvict(value = "cart", key = "#userId") | ||
@Transactional | ||
public CartSummaryResponse deleteCartProduct(Long userId, Long productId) { | ||
User user = userApiRepository.findById(userId) | ||
.orElseThrow(() -> new BadRequestException("User not found")); | ||
Cart cart = user.getCart(); | ||
|
||
CartProduct cartProduct = cartProductApiRepository.findByCart_IdAndProduct_Id(cart.getId(), productId) | ||
.orElseThrow(() -> new BadRequestException("Cart product not found")); | ||
|
||
cartProductApiRepository.delete(cartProduct); | ||
return getCartSummary(user); | ||
} | ||
|
||
|
||
private CartSummaryResponse getCartSummary(User user) { | ||
Cart cart = user.getCart(); | ||
|
||
List<CartProduct> cartProductList = cartProductApiRepository.findByCartId(cart.getId()); | ||
|
||
List<CartProductDto> cartProductDtoList = new ArrayList<>(); | ||
|
||
for (CartProduct cartProduct : cartProductList) { | ||
Product product = cartProduct.getProduct(); | ||
ProductSummaryDto productSummaryDto; | ||
Long quantity = 0L; | ||
CartProductStatus status; | ||
|
||
if (product == null || product.isDeleted()) { | ||
status = CartProductStatus.DELETED; | ||
productSummaryDto = null; | ||
|
||
} else if (product.getStockQuantity() < cartProduct.getQuantity()) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 이 부분이 재고 부족을 체크하는 로직인가요? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 확인 |
||
status = CartProductStatus.SHORTAGE; | ||
productSummaryDto = ProductSummaryDto.of(product); | ||
quantity = product.getStockQuantity(); | ||
|
||
} else { | ||
status = CartProductStatus.AVAILABLE; | ||
productSummaryDto = ProductSummaryDto.of(product); | ||
quantity = cartProduct.getQuantity(); | ||
} | ||
Long totalPrice = (productSummaryDto != null) ? productSummaryDto.getPrice() * cartProduct.getQuantity() : 0; | ||
|
||
cartProductDtoList.add(new CartProductDto( | ||
productSummaryDto, | ||
quantity, | ||
status, | ||
totalPrice | ||
)); | ||
} | ||
return CartSummaryResponse.of(cartProductDtoList); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,59 @@ | ||
package com.example.api.module.product.controller; | ||
|
||
import com.example.api.module.product.controller.request.ProductCreateRequest; | ||
import com.example.api.module.product.controller.request.ProductUpdateRequest; | ||
import com.example.api.module.product.controller.request.ProductSearchConditionRequest; | ||
import com.example.api.module.product.controller.response.ImageUploadResponse; | ||
import com.example.api.module.product.controller.response.ProductResponse; | ||
import com.example.api.module.product.controller.response.ProductsSearchResponse; | ||
import com.example.api.module.product.service.ProductService; | ||
import com.example.core.model.response.DataResponse; | ||
import lombok.RequiredArgsConstructor; | ||
import org.springframework.data.domain.Page; | ||
import org.springframework.web.bind.annotation.*; | ||
import org.springframework.web.multipart.MultipartFile; | ||
|
||
@RestController | ||
@RequiredArgsConstructor | ||
@RequestMapping("/api/v1/products") | ||
public class ProductController { | ||
|
||
private final ProductService productService; | ||
|
||
|
||
//GET /api/v1/products?categoryId={categoryId}&minPrice={minprice}&maxPrice={maxprice}&sort={sortType},{sortBy}&page={page}&size={size} | ||
@GetMapping() | ||
public DataResponse<Page<ProductsSearchResponse>> getProducts(@ModelAttribute ProductSearchConditionRequest condition) { | ||
return DataResponse.of(productService.getProducts(condition)); | ||
} | ||
|
||
@GetMapping("/{productId}") | ||
public DataResponse<ProductResponse> getProduct(@PathVariable("productId") Long productId) { | ||
return DataResponse.of(productService.getProduct(productId)); | ||
} | ||
|
||
// @PreAuthorize("hasAnyRole('SUPER_ADMIN', 'ADMIN')") | ||
@PostMapping("/image") | ||
public DataResponse<ImageUploadResponse> uploadImage(@RequestParam("file") MultipartFile file) { | ||
System.out.println("/image controller"); | ||
return DataResponse.of(productService.uploadImage(file)); | ||
} | ||
|
||
// @PreAuthorize("hasAnyRole('SUPER_ADMIN', 'ADMIN')") | ||
@PostMapping() | ||
public DataResponse<Long> createProduct(@RequestBody ProductCreateRequest request) { | ||
return DataResponse.of(productService.createProduct(request)); | ||
} | ||
|
||
// @PreAuthorize("hasAnyRole('SUPER_ADMIN', 'ADMIN')") | ||
@PutMapping("/{productId}") | ||
public DataResponse<Long> updateProduct(@PathVariable Long productId, @RequestBody ProductUpdateRequest request) { | ||
return DataResponse.of(productService.updateProduct(productId, request)); | ||
} | ||
|
||
// @PreAuthorize("hasAnyRole('SUPER_ADMIN', 'ADMIN')") | ||
@DeleteMapping("/{productId}") | ||
public DataResponse<Long> deleteProduct(@PathVariable Long productId) { | ||
return DataResponse.of(productService.deleteProduct(productId)); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
package com.example.api.module.product.controller.request; | ||
|
||
import lombok.Data; | ||
import org.springframework.web.multipart.MultipartFile; | ||
|
||
@Data | ||
public class ImageUploadRequest { | ||
private String title; | ||
private String url; | ||
private MultipartFile file; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
임시 코드가 반영된 것 같습니다