Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
af90030
feat: 결제 생성, 승인, 취소 및 내역 조회 기능 구현 #30
githyj-jang Mar 6, 2026
9eb0312
feat: PaymentController 및 PaymentService 단위 테스트 추가 #30
githyj-jang Mar 6, 2026
0b35aeb
refactor: refactor payment DTO and controller package 구조 #30
githyj-jang Mar 6, 2026
6132bcb
Merge branch 'develop' into feature/payment
githyj-jang Mar 9, 2026
c6eb50f
feat: implement pagination and search functionality for payments #30
githyj-jang Mar 9, 2026
44adf45
feat: enhance review management with pagination and role-based access…
githyj-jang Mar 10, 2026
37bcf4c
feat: refactor ReviewServiceTest to remove UserPrincipal and implemen…
githyj-jang Mar 10, 2026
3b2e49f
feat: refactor ReviewServiceTest to remove UserPrincipal and implemen…
githyj-jang Mar 10, 2026
6798554
feat: refactor ReviewControllerTest to use nested classes for better …
githyj-jang Mar 10, 2026
6ad58d9
refactor: (Cart) apply CommonResponse + swagger docs
Mar 10, 2026
5c8d5cf
refactor: (Store) apply CommonResponse + swagger docs
Mar 10, 2026
0ce2b5e
Merge branch 'refs/heads/develop' into feature/payment
githyj-jang Mar 10, 2026
c4fd324
Merge pull request #64 from straycat405/main
straycat405 Mar 10, 2026
d00d170
test: (cart) DELETE 응답 테스트 수정
Mar 10, 2026
ec8b4b7
feat: add unit tests for payment cancellation and deletion scenarios
githyj-jang Mar 10, 2026
be765d8
Merge pull request #65 from straycat405/feature/store-cart
jihxonx Mar 10, 2026
d70d995
Merge pull request #66 from straycat405/feature/payment
githyj-jang Mar 10, 2026
3117ea4
Merge branch 'refs/heads/develop' into feature/review
githyj-jang Mar 10, 2026
6da0126
feat: add QueryDslConfig import to UserRepositoryTest for custom repo…
githyj-jang Mar 10, 2026
e74dce7
Merge pull request #67 from straycat405/feature/payment
githyj-jang Mar 10, 2026
b5cc121
Merge branch 'refs/heads/develop' into feature/review
githyj-jang Mar 10, 2026
864293a
feat : 가게 소유권 검증 로직 구현
Mar 10, 2026
da6b21d
Merge branch 'develop' of https://github.com/straycat405/deliverycomm…
Mar 10, 2026
22be97b
Merge pull request #68 from straycat405/feature/order
Jinyoung-Kim96 Mar 10, 2026
7a23bdb
feat: integrate Order entity into Review and update related logic for…
githyj-jang Mar 10, 2026
ce8f55a
Merge branch 'refs/heads/develop' into feature/review
githyj-jang Mar 10, 2026
a5b73b5
Merge pull request #70 from straycat405/feature/review
githyj-jang Mar 10, 2026
0d23530
Merge pull request #73 from straycat405/main
straycat405 Mar 10, 2026
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
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,17 @@
import com.babjo.deliverycommerce.domain.cart.dto.CartItemQuantityUpdateRequestDto;
import com.babjo.deliverycommerce.domain.cart.dto.CartResponseDto;
import com.babjo.deliverycommerce.domain.cart.service.CartService;
import com.babjo.deliverycommerce.global.common.dto.CommonResponse;
import com.babjo.deliverycommerce.global.security.CurrentUserResolver;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.media.Content;
import io.swagger.v3.oas.annotations.media.Schema;
import io.swagger.v3.oas.annotations.responses.ApiResponse;
import io.swagger.v3.oas.annotations.responses.ApiResponses;
import jakarta.validation.Valid;
import lombok.extern.slf4j.Slf4j;
import org.checkerframework.checker.units.qual.A;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.security.core.Authentication;
Expand All @@ -28,8 +36,20 @@ public CartController(CartService cartService, CurrentUserResolver currentUserRe
this.currentUserResolver = currentUserResolver;
}

@Operation(
summary = "내 장바구니 조회",
description = """
- 로그인 사용자의 장바구니를 조회 합니다.
- 장바구니가 없으면 empty 응답을 반환합니다.
"""
)
@ApiResponses({
@ApiResponse(responseCode = "200", description = "조회 성공"),
@ApiResponse(responseCode = "401", description = "인증 실패")
})

@GetMapping
public CartResponseDto getMyCart(Authentication authentication) {
public ResponseEntity<CommonResponse<CartResponseDto>> getMyCart(Authentication authentication) {
Long userId = currentUserResolver.getUserId(authentication);

log.info("내 장바구니 조회 API 요청: userId={}", userId);
Expand All @@ -38,13 +58,29 @@ public CartResponseDto getMyCart(Authentication authentication) {

log.info("내 장바구니 조회 API 완료: userId={}, cartId={}, itemCount={}", userId, response.getCartId(), response.getItems().size());

return response;
return CommonResponse.ok("내 장바구니 조회 성공", response);
}

@Operation(
summary = "장바구니 상품 수량 변경",
description = """
- 특정 장바구니 항목(cartItemId)의 수량을 변경합니다.
- quantity는 1 이상이어야 합니다.
- 본인 소유 장바구니 항목인지 검증합니다.
"""
)
@ApiResponses({
@ApiResponse(responseCode = "200", description = "수정 성공"),
@ApiResponse(responseCode = "400", description = "요청 값 검증 실패(수량 1 미만 등)"),
@ApiResponse(responseCode = "403", description = "권한 없음(본인 장바구니 항목 아님)"),
@ApiResponse(responseCode = "404", description = "장바구니 항목이 존재하지 않음"),
@ApiResponse(responseCode = "401", description = "인증 실패")
})

@PatchMapping("/items/{cartItemId}")
public CartResponseDto updateQuantity(@PathVariable UUID cartItemId,
@Valid @RequestBody CartItemQuantityUpdateRequestDto request,
Authentication authentication) {
public ResponseEntity<CommonResponse<CartResponseDto>> updateQuantity(@PathVariable UUID cartItemId,
@Valid @RequestBody CartItemQuantityUpdateRequestDto request,
Authentication authentication) {

Long userId = currentUserResolver.getUserId(authentication);

Expand All @@ -54,12 +90,26 @@ public CartResponseDto updateQuantity(@PathVariable UUID cartItemId,

log.info("장바구니 수량 수정 API 완료: userId={}, cartId={}, itemCount={}", userId, response.getCartId(), response.getItems().size());

return response;
return CommonResponse.ok("장바구니 수량 수정 성공",response);
}

@Operation(
summary = "장바구니 상품 삭제",
description = """
- 특정 장바구니 항목을 삭제합니다.(Soft Delete)
- 본인 소유 장바구니 항목이 아니면 403 반환
"""
)
@ApiResponses({
@ApiResponse(responseCode = "200", description = "삭제 성공"),
@ApiResponse(responseCode = "403", description = "권한 없음(본인 장바구니 항목 아님)"),
@ApiResponse(responseCode = "404", description = "장바구니 항목이 존재하지 않음"),
@ApiResponse(responseCode = "401", description = "인증 실패")
})

@DeleteMapping("/items/{cartItemId}")
public ResponseEntity<Void> deleteItem(@PathVariable UUID cartItemId,
Authentication authentication) {
public ResponseEntity<CommonResponse<Void>> deleteItem(@PathVariable UUID cartItemId,
Authentication authentication) {

Long userId = currentUserResolver.getUserId(authentication);

Expand All @@ -69,11 +119,23 @@ public ResponseEntity<Void> deleteItem(@PathVariable UUID cartItemId,

log.info("장바구니 항목 삭제 API 완료: userId={}, cartItemId={}", userId, cartItemId);

return ResponseEntity.noContent().build();
return CommonResponse.ok("장바구니 항목 삭제 성공", null);
}

@Operation(
summary = "장바구니 비우기",
description = """
- 로그인 사용자의 장바구니 항목을 전체 삭제합니다.(Soft Delete)
- 장바구니가 비어있어도 성공 처리합니다.
"""
)
@ApiResponses({
@ApiResponse(responseCode = "200", description = "비우기 성공"),
@ApiResponse(responseCode = "401", description = "인증 실패")
})

@DeleteMapping
public ResponseEntity<Void> clearCart(Authentication authentication) {
public ResponseEntity<CommonResponse<Void>> clearCart(Authentication authentication) {
Long userId = currentUserResolver.getUserId(authentication);

log.info("장바구니 비우기 API 요청: userId={}", userId);
Expand All @@ -82,12 +144,30 @@ public ResponseEntity<Void> clearCart(Authentication authentication) {

log.info("장바구니 비우기 API 완료: userId={}", userId);

return ResponseEntity.noContent().build();
return CommonResponse.ok("장바구니 비우기 성공", null);
}

@Operation(
summary = "장바구니 상품 추가",
description = """
- 로그인 사용자의 장바구니에 상품을 추가합니다.
- 동일 상품이 이미 담겨 있으면 수량을 증가시킵니다.
- quantity가 null이면 기본값 1로 처리합니다.
- 상품이 존재하지 않거나 삭제된 상품이면 404를 반환합니다.
- 단일 가게 정책 위반 시 CART_STORE_MISMATCH 예외를 반환합니다.
"""
)
@ApiResponses({
@ApiResponse(responseCode = "200", description = "추가 성공"),
@ApiResponse(responseCode = "400", description = "요청 값 검증 실패(수량 1 미만 등)"),
@ApiResponse(responseCode = "404", description = "상품이 존재하지 않음/삭제됨"),
@ApiResponse(responseCode = "409", description = "단일 가게 정책 위반(CART_STORE_MISMATCH)"),
@ApiResponse(responseCode = "401", description = "인증 실패")
})

@PostMapping("/items")
public CartResponseDto addItem(@Valid @RequestBody CartItemAddRequestDto request,
Authentication authentication) {
public ResponseEntity<CommonResponse<CartResponseDto>> addItem(@Valid @RequestBody CartItemAddRequestDto request,
Authentication authentication) {

Long userId = currentUserResolver.getUserId(authentication);

Expand All @@ -97,7 +177,7 @@ public CartResponseDto addItem(@Valid @RequestBody CartItemAddRequestDto request

log.info("장바구니 상품 추가 API 완료: userId={}, cartId={}, itemCount={}", userId, response.getCartId(), response.getItems().size());

return response;
return CommonResponse.ok("장바구니 상품 추가 성공", response);

}
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@
import com.babjo.deliverycommerce.domain.order.repository.OrderRepository;
import com.babjo.deliverycommerce.domain.product.entity.Product;
import com.babjo.deliverycommerce.domain.product.repository.ProductRepository;
import com.babjo.deliverycommerce.domain.store.entity.Store;
import com.babjo.deliverycommerce.domain.store.repository.StoreRepository;
import com.babjo.deliverycommerce.global.exception.CustomException;
import com.babjo.deliverycommerce.global.exception.ErrorCode;
import lombok.RequiredArgsConstructor;
Expand All @@ -27,6 +29,8 @@ public class OrderService {

private final OrderRepository orderRepository;
private final ProductRepository productRepository;
private final StoreRepository storeRepository;

// 주문 생성
@Transactional
public OrderResponseDto.OrderDetail createOrder(Long userId, OrderRequestDto.CreateOrder request){
Expand Down Expand Up @@ -108,6 +112,7 @@ public OrderResponseDto.OrderAction softDeleteOrder(UUID orderId, Long userId){
@Transactional
public OrderResponseDto.OrderAction acceptOrder(UUID orderId, Long ownerId, OrderRequestDto.AcceptOrder request){
Order order = findActiveOrder(orderId);
validateStoreOwner(order.getStoreId(),ownerId);
order.accept(ownerId, request.getCookingMinutes());

return OrderResponseDto.OrderAction.from(order, order.getAcceptedAt());
Expand All @@ -117,20 +122,30 @@ public OrderResponseDto.OrderAction acceptOrder(UUID orderId, Long ownerId, Orde
@Transactional
public OrderResponseDto.OrderAction updateOrderStatus(UUID orderId,Long ownerId, OrderStatus status){
Order order = findActiveOrder(orderId);
validateStoreOwner(order.getStoreId(),ownerId);
switch (status){
case PREPARING -> order.startPreparing(ownerId);
case PICKUP_READY -> order.readyPickup(ownerId);
case PICKED_UP -> order.completePickup(ownerId);
default -> throw new CustomException(ErrorCode.INVALID_ORDER_STATUS);
}
return OrderResponseDto.OrderAction.from(order, LocalDateTime.now());
return OrderResponseDto.OrderAction.from(order, order.getUpdatedAt());
}

// orderId 조회 로직 공통화
// orderId 조회 로직 공통 메소드
private Order findActiveOrder(UUID orderId){
return orderRepository.findById(orderId)
.orElseThrow(() -> new CustomException(ErrorCode.ORDER_NOT_FOUND));
}

// 가게 소유권 검증 로직 공통 메소드
private void validateStoreOwner(UUID storeId, Long ownerId){
Store store = storeRepository.findByStoreIdAndDeletedAtIsNull(storeId)
.orElseThrow(() -> new CustomException(ErrorCode.STORE_NOT_FOUND));
if(!store.getOwnerId().equals(ownerId)){
throw new CustomException(ErrorCode.STORE_FORBIDDEN);
}
}


}
Loading