Skip to content
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
46 changes: 46 additions & 0 deletions src/docs/asciidoc/_cart.adoc
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
== ์žฅ๋ฐ”๊ตฌ๋‹ˆ (Cart) API

=== 1. ์žฅ๋ฐ”๊ตฌ๋‹ˆ ์ถ”๊ฐ€
`POST /carts`

์š”์ฒญ ๋ณธ๋ฌธ:
include::{snippets}/cart-create/request-fields.adoc[]

์‘๋‹ต:
include::{snippets}/cart-create/http-response.adoc[]

---

=== 2. ์žฅ๋ฐ”๊ตฌ๋‹ˆ ๋ชฉ๋ก ์กฐํšŒ
`GET /carts`

์‘๋‹ต ๋ณธ๋ฌธ:
include::{snippets}/cart-get-list/response-fields.adoc[]

์‘๋‹ต:
include::{snippets}/cart-get-list/http-response.adoc[]

---

=== 3. ์žฅ๋ฐ”๊ตฌ๋‹ˆ ์ˆ˜๋Ÿ‰ ์ˆ˜์ •
`PATCH /carts/{cartId}`

๊ฒฝ๋กœ ํŒŒ๋ผ๋ฏธํ„ฐ (Path Parameters):
include::{snippets}/cart-update/path-parameters.adoc[]

์š”์ฒญ ๋ณธ๋ฌธ:
include::{snippets}/cart-update/request-fields.adoc[]

์‘๋‹ต:
include::{snippets}/cart-update/http-response.adoc[]

---

=== 4. ์žฅ๋ฐ”๊ตฌ๋‹ˆ ์‚ญ์ œ
`DELETE /carts/{cartId}`

๊ฒฝ๋กœ ํŒŒ๋ผ๋ฏธํ„ฐ (Path Parameters):
include::{snippets}/cart-delete/path-parameters.adoc[]

์‘๋‹ต:
include::{snippets}/cart-delete/http-response.adoc[]
2 changes: 2 additions & 0 deletions src/docs/asciidoc/index.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -16,3 +16,5 @@ include::_discount.adoc[]
include::_deliverypolicy.adoc[]
// include::_product.adoc[]

include::_cart.adoc[]

Original file line number Diff line number Diff line change
Expand Up @@ -15,86 +15,59 @@
import java.util.UUID;
import java.util.stream.Collectors;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

@Service
@RequiredArgsConstructor
@Transactional
@Slf4j
public class CartService {

private final CartRepository cartRepository;
private final ProductOptionValueRepository productOptionValueRepository;
private final MemberUtil memberUtil;

@Transactional
public CartResponse createCart(CartCreateRequest request) {
Member currentMember = memberUtil.getCurrentMember();

// ์˜ต์…˜๊ฐ’ ๊ฒ€์ฆ
ProductOptionValue optionValue =
productOptionValueRepository
.findById(request.optionValueId())
.orElseThrow(
() -> new CommonException(CartErrorCode.OPTION_VALUE_NOT_FOUND));

// ๊ธฐ์กด ๋™์ผ ์˜ต์…˜ Cart ์กด์žฌ ์‹œ ์ˆ˜๋Ÿ‰ ํ•ฉ์‚ฐ
Cart existing =
cartRepository.findByMemberIdAndOptionValueId(
currentMember.getMemberId(), request.optionValueId());

Cart saved;
Cart target;
if (existing != null) {
int oldQuantity = existing.getQuantity();
int updatedQuantity = oldQuantity + request.quantity();
int updatedQuantity = existing.getQuantity() + request.quantity();
existing.updateQuantity(updatedQuantity);
saved = existing;

log.info(
"์žฅ๋ฐ”๊ตฌ๋‹ˆ ์ˆ˜๋Ÿ‰ ํ•ฉ์‚ฐ: memberId={}, optionValueId={}, oldQuantity={}, newQuantity={}",
currentMember.getMemberId(),
request.optionValueId(),
oldQuantity,
updatedQuantity);
target = existing;
} else {
Cart newCart = Cart.createCart(currentMember, optionValue, request.quantity());
saved = cartRepository.save(newCart);

log.info(
"์žฅ๋ฐ”๊ตฌ๋‹ˆ ์‹ ๊ทœ ์ถ”๊ฐ€: memberId={}, optionValueId={}, quantity={}",
currentMember.getMemberId(),
request.optionValueId(),
request.quantity());
target = Cart.createCart(currentMember, optionValue, request.quantity());
cartRepository.save(target);
}

return CartResponse.from(saved);
return CartResponse.from(target);
}

public CartResponse updateCart(UUID cartId, CartUpdateRequest request) {
public void updateCart(UUID cartId, CartUpdateRequest request) {
Cart cart =
cartRepository
.findById(cartId)
.orElseThrow(() -> new CommonException(CartErrorCode.CART_NOT_FOUND));

memberUtil.assertMemberResourceAccess(cart.getMember());

if (cart.getQuantity().equals(request.quantity())) {
throw new CommonException(CartErrorCode.CART_NOT_MODIFIED);
}

cart.updateQuantity(request.quantity());
log.info("์žฅ๋ฐ”๊ตฌ๋‹ˆ ์ˆ˜์ • ์™„๋ฃŒ: cartId={}, updatedQuantity={}", cartId, request.quantity());
return CartResponse.from(cart);
}

@Transactional(readOnly = true)
public List<CartResponse> getCartListByMember() {
Member currentMember = memberUtil.getCurrentMember();

List<Cart> carts = cartRepository.findAllWithProductByMemberId(currentMember.getMemberId());
log.info("์žฅ๋ฐ”๊ตฌ๋‹ˆ ์กฐํšŒ ์™„๋ฃŒ: memberId={}, count={}", currentMember.getMemberId(), carts.size());

return carts.stream().map(CartResponse::from).collect(Collectors.toList());
}

Expand All @@ -103,10 +76,7 @@ public void deleteCart(UUID cartId) {
cartRepository
.findById(cartId)
.orElseThrow(() -> new CommonException(CartErrorCode.CART_NOT_FOUND));

memberUtil.assertMemberResourceAccess(cart.getMember());
cartRepository.delete(cart);

log.info("์žฅ๋ฐ”๊ตฌ๋‹ˆ ์‚ญ์ œ ์™„๋ฃŒ: cartId={}", cartId);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -23,15 +23,18 @@ public interface CartRepository extends JpaRepository<Cart, UUID> {
Cart findByMemberIdAndOptionValueId(
@Param("memberId") @NotNull Long memberId, @Param("optionValueId") UUID optionValueId);

// ํŠน์ • ํšŒ์›์˜ ์žฅ๋ฐ”๊ตฌ๋‹ˆ + ์ƒํ’ˆ/์˜ต์…˜ ํ†ตํ•ฉ ์กฐํšŒ
// TODO: ์ด๋ฏธ์ง€ ์กฐ์ธ ์ถ”ํ›„ ์ถ”๊ฐ€ ์˜ˆ์ •
/**
* ํŠน์ • ํšŒ์›์˜ ์žฅ๋ฐ”๊ตฌ๋‹ˆ + ์ƒํ’ˆ/์˜ต์…˜/์ด๋ฏธ์ง€ ํ†ตํ•ฉ ์กฐํšŒ - Cart โ†’ OptionValue โ†’ OptionGroup โ†’ Product โ†’ ProductImage -
* LEFT JOIN FETCH ๋กœ ๋Œ€ํ‘œ ์ด๋ฏธ์ง€๊นŒ์ง€ ๋ฏธ๋ฆฌ ๋กœ๋”ฉ
*/
@Query(
"""
SELECT DISTINCT c
FROM Cart c
JOIN FETCH c.optionValue ov
JOIN FETCH ov.optionGroup og
JOIN FETCH og.product p
LEFT JOIN FETCH p.productImages pi
WHERE c.member.memberId = :memberId
""")
List<Cart> findAllWithProductByMemberId(@Param("memberId") Long memberId);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@
import java.util.UUID;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;

@RestController
Expand All @@ -20,30 +22,35 @@ public class CartController {
private final CartService cartService;

@PostMapping
public CartResponse createCart(@Valid @RequestBody CartCreateRequest request) {
public ResponseEntity<CartResponse> createCart(@Valid @RequestBody CartCreateRequest request) {
log.info(
"์žฅ๋ฐ”๊ตฌ๋‹ˆ ์ถ”๊ฐ€ ์š”์ฒญ: optionValueId={}, quantity={}",
request.optionValueId(),
request.quantity());
return cartService.createCart(request);
CartResponse response = cartService.createCart(request);
return ResponseEntity.status(HttpStatus.CREATED).body(response);
}

@PatchMapping("/{cartId}")
public CartResponse updateCart(
public ResponseEntity<Void> updateCart(
@PathVariable UUID cartId, @Valid @RequestBody CartUpdateRequest request) {
log.info("์žฅ๋ฐ”๊ตฌ๋‹ˆ ์ˆ˜์ • ์š”์ฒญ: cartId={}, quantity={}", cartId, request.quantity());
return cartService.updateCart(cartId, request);
cartService.updateCart(cartId, request);
return ResponseEntity.noContent().build();
}

@GetMapping
public List<CartResponse> getMyCartList() {
log.info("๋ณธ์ธ ์žฅ๋ฐ”๊ตฌ๋‹ˆ ์กฐํšŒ ์š”์ฒญ");
return cartService.getCartListByMember();
public ResponseEntity<List<CartResponse>> getMyCartList() {
log.info("์žฅ๋ฐ”๊ตฌ๋‹ˆ ๋ชฉ๋ก ์กฐํšŒ ์š”์ฒญ");
List<CartResponse> responses = cartService.getCartListByMember();
log.info("์žฅ๋ฐ”๊ตฌ๋‹ˆ ์กฐํšŒ ๊ฒฐ๊ณผ: {}๊ฐœ ํ•ญ๋ชฉ", responses.size());
return ResponseEntity.ok(responses);
}

@DeleteMapping("/{cartId}")
public void deleteCart(@PathVariable UUID cartId) {
public ResponseEntity<Void> deleteCart(@PathVariable UUID cartId) {
log.info("์žฅ๋ฐ”๊ตฌ๋‹ˆ ์‚ญ์ œ ์š”์ฒญ: cartId={}", cartId);
cartService.deleteCart(cartId);
return ResponseEntity.noContent().build();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,5 @@
import java.util.UUID;

public record CartCreateRequest(
@NotNull(message = "ํšŒ์› ID๋Š” ํ•„์ˆ˜ ์ž…๋ ฅ๊ฐ’์ž…๋‹ˆ๋‹ค.") Long memberId,
@NotNull(message = "์˜ต์…˜ ๊ฐ’ ID๋Š” ํ•„์ˆ˜ ์ž…๋ ฅ๊ฐ’์ž…๋‹ˆ๋‹ค.") UUID optionValueId,
@Min(value = 1, message = "์ˆ˜๋Ÿ‰์€ 1๊ฐœ ์ด์ƒ์ด์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.") int quantity) {}
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ public record CartResponse(
UUID optionValueId,
String productName,
String optionValueName,
// String imageUrl, // ์ƒํ’ˆ ์ด๋ฏธ์ง€ ๊ตฌํ˜„ ํ›„ ์ฃผ์„ ํ•ด์ œ
String imageUrl,
int quantity,
int basePrice, // ์ƒํ’ˆ ๊ธฐ๋ณธ๊ฐ€
int extraPrice, // ์˜ต์…˜ ์ถ”๊ฐ€๊ธˆ
Expand All @@ -26,17 +26,17 @@ public static CartResponse from(Cart cart) {
int unit = base + extra;
int total = unit * cart.getQuantity();

// String imageUrl = (product.getProductImages() != null &&
// !product.getProductImages().isEmpty())
// ? product.getProductImages().get(0).getImageUrl()
// : null;
String imageUrl =
(product.getProductImages() != null && !product.getProductImages().isEmpty())
? product.getProductImages().get(0).getImageUrl()
: null;

return CartResponse.builder()
.cartId(cart.getId())
.optionValueId(optionValue.getId())
.productName(product.getName())
.optionValueName(optionValue.getName())
// .imageUrl(imageUrl)
.imageUrl(imageUrl)
.quantity(cart.getQuantity())
.basePrice(base)
.extraPrice(extra)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -132,5 +132,8 @@ public void updateCategory(Category category) {
this.category = category;
}

// TODO: ์ด๋ฏธ์ง€ ๋งคํ•‘, ๋ฆฌ๋ทฐ ๋งคํ•‘
@OneToMany(mappedBy = "product", cascade = CascadeType.ALL, orphanRemoval = true)
private List<ProductImage> productImages = new ArrayList<>();

// TODO: ๋ฆฌ๋ทฐ ๋งคํ•‘
}
Loading