Skip to content
Closed
Show file tree
Hide file tree
Changes from all 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: 0 additions & 46 deletions src/docs/asciidoc/_cart.adoc

This file was deleted.

2 changes: 0 additions & 2 deletions src/docs/asciidoc/index.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -23,5 +23,3 @@ include::_owner_order.adoc[]
// include::_product.adoc[]
include::_product_image.adoc[]

include::_cart.adoc[]

Original file line number Diff line number Diff line change
Expand Up @@ -15,59 +15,86 @@
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 target;
Cart saved;
if (existing != null) {
int updatedQuantity = existing.getQuantity() + request.quantity();
int oldQuantity = existing.getQuantity();
int updatedQuantity = oldQuantity + request.quantity();
existing.updateQuantity(updatedQuantity);
target = existing;
saved = existing;

log.info(
"장바구니 수량 합산: memberId={}, optionValueId={}, oldQuantity={}, newQuantity={}",
currentMember.getMemberId(),
request.optionValueId(),
oldQuantity,
updatedQuantity);
} else {
target = Cart.createCart(currentMember, optionValue, request.quantity());
cartRepository.save(target);
Cart newCart = Cart.createCart(currentMember, optionValue, request.quantity());
saved = cartRepository.save(newCart);

log.info(
"장바구니 신규 추가: memberId={}, optionValueId={}, quantity={}",
currentMember.getMemberId(),
request.optionValueId(),
request.quantity());
}

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

public void updateCart(UUID cartId, CartUpdateRequest request) {
public CartResponse 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 @@ -76,7 +103,10 @@ 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,18 +23,15 @@ public interface CartRepository extends JpaRepository<Cart, UUID> {
Cart findByMemberIdAndOptionValueId(
@Param("memberId") @NotNull Long memberId, @Param("optionValueId") UUID optionValueId);

/**
* 특정 회원의 장바구니 + 상품/옵션/이미지 통합 조회 - Cart → OptionValue → OptionGroup → Product → ProductImage -
* LEFT JOIN FETCH 로 대표 이미지까지 미리 로딩
*/
// 특정 회원의 장바구니 + 상품/옵션 통합 조회
// TODO: 이미지 조인 추후 추가 예정
@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,8 +9,6 @@
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 @@ -22,35 +20,30 @@ public class CartController {
private final CartService cartService;

@PostMapping
public ResponseEntity<CartResponse> createCart(@Valid @RequestBody CartCreateRequest request) {
public CartResponse createCart(@Valid @RequestBody CartCreateRequest request) {
log.info(
"장바구니 추가 요청: optionValueId={}, quantity={}",
request.optionValueId(),
request.quantity());
CartResponse response = cartService.createCart(request);
return ResponseEntity.status(HttpStatus.CREATED).body(response);
return cartService.createCart(request);
}

@PatchMapping("/{cartId}")
public ResponseEntity<Void> updateCart(
public CartResponse updateCart(
@PathVariable UUID cartId, @Valid @RequestBody CartUpdateRequest request) {
log.info("장바구니 수정 요청: cartId={}, quantity={}", cartId, request.quantity());
cartService.updateCart(cartId, request);
return ResponseEntity.noContent().build();
return cartService.updateCart(cartId, request);
}

@GetMapping
public ResponseEntity<List<CartResponse>> getMyCartList() {
log.info("장바구니 목록 조회 요청");
List<CartResponse> responses = cartService.getCartListByMember();
log.info("장바구니 조회 결과: {}개 항목", responses.size());
return ResponseEntity.ok(responses);
public List<CartResponse> getMyCartList() {
log.info("본인 장바구니 조회 요청");
return cartService.getCartListByMember();
}

@DeleteMapping("/{cartId}")
public ResponseEntity<Void> deleteCart(@PathVariable UUID cartId) {
public 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,5 +5,6 @@
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,8 +132,5 @@ public void updateCategory(Category category) {
this.category = category;
}

@OneToMany(mappedBy = "product", cascade = CascadeType.ALL, orphanRemoval = true)
private List<ProductImage> productImages = new ArrayList<>();

// TODO: 리뷰 매핑
// TODO: 이미지 매핑, 리뷰 매핑
}
Loading