From 7395c32e26b06b8bcac107319e9467bee5375142 Mon Sep 17 00:00:00 2001 From: kenduNMT Date: Tue, 18 Nov 2025 16:19:16 +0700 Subject: [PATCH] order --- .../controller/OrderController.java | 200 ++++++++++++++++++ .../model/dto/request/CreateOrderRequest.java | 10 + .../model/dto/request/OrderItemRequest.java | 9 + .../model/dto/response/CartItemResponse.java | 24 ++- .../model/dto/response/OrderItemResponse.java | 14 ++ .../dto/response/OrderProgressResponse.java | 11 + .../model/dto/response/OrderResponse.java | 19 ++ .../model/dto/response/StatusTimeline.java | 11 + .../model/entity/Order.java | 21 +- .../model/entity/OrderItem.java | 6 +- .../repository/CartItemRepository.java | 4 + .../repository/CartRepository.java | 13 +- .../repository/OrderRepository.java | 19 +- .../service/AuthService.java | 6 + .../service/CartService.java | 69 +++--- .../service/OrderService.java | 193 +++++++++++++++++ 16 files changed, 573 insertions(+), 56 deletions(-) create mode 100644 src/main/java/com/example/project_it207_server/controller/OrderController.java create mode 100644 src/main/java/com/example/project_it207_server/model/dto/request/CreateOrderRequest.java create mode 100644 src/main/java/com/example/project_it207_server/model/dto/request/OrderItemRequest.java create mode 100644 src/main/java/com/example/project_it207_server/model/dto/response/OrderItemResponse.java create mode 100644 src/main/java/com/example/project_it207_server/model/dto/response/OrderProgressResponse.java create mode 100644 src/main/java/com/example/project_it207_server/model/dto/response/OrderResponse.java create mode 100644 src/main/java/com/example/project_it207_server/model/dto/response/StatusTimeline.java create mode 100644 src/main/java/com/example/project_it207_server/service/OrderService.java diff --git a/src/main/java/com/example/project_it207_server/controller/OrderController.java b/src/main/java/com/example/project_it207_server/controller/OrderController.java new file mode 100644 index 0000000..56f2beb --- /dev/null +++ b/src/main/java/com/example/project_it207_server/controller/OrderController.java @@ -0,0 +1,200 @@ +package com.example.project_it207_server.controller; + +import com.example.project_it207_server.model.dto.request.CreateOrderRequest; +import com.example.project_it207_server.model.dto.response.OrderProgressResponse; +import com.example.project_it207_server.model.dto.response.OrderResponse; +import com.example.project_it207_server.service.AuthService; +import com.example.project_it207_server.service.OrderService; +import lombok.RequiredArgsConstructor; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.security.core.Authentication; +import org.springframework.web.bind.annotation.*; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +@RestController +@RequestMapping("/api/orders") +@RequiredArgsConstructor +public class OrderController { + + private final OrderService orderService; + private final AuthService authService; + + /** + * Tạo đơn hàng mới + * POST /api/orders + */ + @PostMapping("/{userId}") + public ResponseEntity> createOrder( + @PathVariable Long userId, + @RequestBody CreateOrderRequest request) { + try { + OrderResponse order = orderService.createOrder(userId, request); + + Map response = new HashMap<>(); + response.put("success", true); + response.put("message", "Order created successfully"); + response.put("data", order); + + return ResponseEntity.status(HttpStatus.CREATED).body(response); + } catch (Exception e) { + Map response = new HashMap<>(); + response.put("success", false); + response.put("message", e.getMessage()); + return ResponseEntity.badRequest().body(response); + } + } + + /** + * Lấy danh sách tất cả đơn hàng + * GET /api/orders + */ + @GetMapping + public ResponseEntity> getAllOrders(Authentication authentication) { + try { + // Lấy email từ token + String email = authentication.getName(); + + // Lấy userId từ email + Long userId = authService.getUserIdByEmail(email); + List orders = orderService.getAllOrders(userId); + + Map response = new HashMap<>(); + response.put("success", true); + response.put("data", orders); + response.put("total", orders.size()); + + return ResponseEntity.ok(response); + } catch (Exception e) { + Map response = new HashMap<>(); + response.put("success", false); + response.put("message", e.getMessage()); + return ResponseEntity.badRequest().body(response); + } + } + + /** + * Lọc đơn hàng theo trạng thái + * GET /api/orders/filter?status={status} + * Status: PENDING, CONFIRMED, SHIPPING, DELIVERED, CANCELLED + */ + @GetMapping("/filter") + public ResponseEntity> getOrdersByStatus( + @RequestParam String status, + Authentication authentication) { + try { + // Lấy email từ token + String email = authentication.getName(); + + // Lấy userId từ email + Long userId = authService.getUserIdByEmail(email); + List orders = orderService.getOrdersByStatus(userId, status.toUpperCase()); + + Map response = new HashMap<>(); + response.put("success", true); + response.put("data", orders); + response.put("status", status); + response.put("total", orders.size()); + + return ResponseEntity.ok(response); + } catch (Exception e) { + Map response = new HashMap<>(); + response.put("success", false); + response.put("message", e.getMessage()); + return ResponseEntity.badRequest().body(response); + } + } + + /** + * Xem chi tiết đơn hàng + * GET /api/orders/{orderId} + */ + @GetMapping("/{orderId}") + public ResponseEntity> getOrderDetail( + @PathVariable Long orderId, + Authentication authentication) { + try { + // Lấy email từ token + String email = authentication.getName(); + + // Lấy userId từ email + Long userId = authService.getUserIdByEmail(email); + + OrderResponse order = orderService.getOrderDetail(orderId, userId); + + Map response = new HashMap<>(); + response.put("success", true); + response.put("data", order); + + return ResponseEntity.ok(response); + } catch (Exception e) { + Map response = new HashMap<>(); + response.put("success", false); + response.put("message", e.getMessage()); + return ResponseEntity.badRequest().body(response); + } + } + + + /** + * Xem tiến độ đơn hàng + * GET /api/orders/{orderId}/progress + */ + @GetMapping("/{orderId}/progress") + public ResponseEntity> getOrderProgress( + @PathVariable Long orderId, + Authentication authentication) { + try { + // Lấy email từ token + String email = authentication.getName(); + + // Lấy userId từ email + Long userId = authService.getUserIdByEmail(email); + OrderProgressResponse progress = orderService.getOrderProgress(orderId, userId); + + Map response = new HashMap<>(); + response.put("success", true); + response.put("data", progress); + + return ResponseEntity.ok(response); + } catch (Exception e) { + Map response = new HashMap<>(); + response.put("success", false); + response.put("message", e.getMessage()); + return ResponseEntity.badRequest().body(response); + } + } + + /** + * Hủy đơn hàng + * PUT /api/orders/{orderId}/cancel + */ + @PutMapping("/{orderId}/cancel") + public ResponseEntity> cancelOrder( + @PathVariable Long orderId, + Authentication authentication) { + try { + // Lấy email từ token + String email = authentication.getName(); + + // Lấy userId từ email + Long userId = authService.getUserIdByEmail(email); + OrderResponse order = orderService.cancelOrder(orderId, userId); + + Map response = new HashMap<>(); + response.put("success", true); + response.put("message", "Order cancelled successfully"); + response.put("data", order); + + return ResponseEntity.ok(response); + } catch (Exception e) { + Map response = new HashMap<>(); + response.put("success", false); + response.put("message", e.getMessage()); + return ResponseEntity.badRequest().body(response); + } + } +} \ No newline at end of file diff --git a/src/main/java/com/example/project_it207_server/model/dto/request/CreateOrderRequest.java b/src/main/java/com/example/project_it207_server/model/dto/request/CreateOrderRequest.java new file mode 100644 index 0000000..c65bac1 --- /dev/null +++ b/src/main/java/com/example/project_it207_server/model/dto/request/CreateOrderRequest.java @@ -0,0 +1,10 @@ +package com.example.project_it207_server.model.dto.request; + +import lombok.*; +import java.util.List; + +@Getter @Setter @NoArgsConstructor @AllArgsConstructor @Builder +public class CreateOrderRequest { + private String shippingAddress; + private List items; +} \ No newline at end of file diff --git a/src/main/java/com/example/project_it207_server/model/dto/request/OrderItemRequest.java b/src/main/java/com/example/project_it207_server/model/dto/request/OrderItemRequest.java new file mode 100644 index 0000000..a7f3ebc --- /dev/null +++ b/src/main/java/com/example/project_it207_server/model/dto/request/OrderItemRequest.java @@ -0,0 +1,9 @@ +package com.example.project_it207_server.model.dto.request; + +import lombok.*; + +@Getter @Setter @NoArgsConstructor @AllArgsConstructor +public class OrderItemRequest { + private Long productId; + private int quantity; +} \ No newline at end of file diff --git a/src/main/java/com/example/project_it207_server/model/dto/response/CartItemResponse.java b/src/main/java/com/example/project_it207_server/model/dto/response/CartItemResponse.java index c19cdec..57f0913 100644 --- a/src/main/java/com/example/project_it207_server/model/dto/response/CartItemResponse.java +++ b/src/main/java/com/example/project_it207_server/model/dto/response/CartItemResponse.java @@ -10,10 +10,22 @@ import java.math.BigDecimal; @Builder public class CartItemResponse { private Long cartItemId; - private Long productId; - private String productName; - private String productImage; private Integer quantity; - private BigDecimal price; - private BigDecimal subtotal; // price * quantity -} \ No newline at end of file + + // Gộp lại thành object product để UI dùng item.product.xxx + private ProductInCart product; + + @Getter + @Setter + @NoArgsConstructor + @AllArgsConstructor + @Builder + public static class ProductInCart { + private Long productId; + private String productName; + private String imageUrl; + private BigDecimal price; + } + + private BigDecimal subtotal; +} diff --git a/src/main/java/com/example/project_it207_server/model/dto/response/OrderItemResponse.java b/src/main/java/com/example/project_it207_server/model/dto/response/OrderItemResponse.java new file mode 100644 index 0000000..72cac05 --- /dev/null +++ b/src/main/java/com/example/project_it207_server/model/dto/response/OrderItemResponse.java @@ -0,0 +1,14 @@ +package com.example.project_it207_server.model.dto.response; + +import lombok.*; +import java.math.BigDecimal; + +@Getter @Setter @NoArgsConstructor @AllArgsConstructor @Builder +public class OrderItemResponse { + private Long orderItemId; + private Long productId; + private String productName; + private int quantity; + private BigDecimal price; + private BigDecimal subtotal; +} \ No newline at end of file diff --git a/src/main/java/com/example/project_it207_server/model/dto/response/OrderProgressResponse.java b/src/main/java/com/example/project_it207_server/model/dto/response/OrderProgressResponse.java new file mode 100644 index 0000000..a2d4a15 --- /dev/null +++ b/src/main/java/com/example/project_it207_server/model/dto/response/OrderProgressResponse.java @@ -0,0 +1,11 @@ +package com.example.project_it207_server.model.dto.response; + +import lombok.*; +import java.util.List; + +@Getter @Setter @NoArgsConstructor @AllArgsConstructor @Builder +public class OrderProgressResponse { + private Long orderId; + private String orderStatus; + private List timeline; +} \ No newline at end of file diff --git a/src/main/java/com/example/project_it207_server/model/dto/response/OrderResponse.java b/src/main/java/com/example/project_it207_server/model/dto/response/OrderResponse.java new file mode 100644 index 0000000..98af5be --- /dev/null +++ b/src/main/java/com/example/project_it207_server/model/dto/response/OrderResponse.java @@ -0,0 +1,19 @@ +package com.example.project_it207_server.model.dto.response; + +import lombok.*; +import java.math.BigDecimal; +import java.time.LocalDateTime; +import java.util.List; + +@Getter @Setter @NoArgsConstructor @AllArgsConstructor @Builder +public class OrderResponse { + private Long orderId; + private Long userId; + private String userName; + private BigDecimal totalPrice; + private String orderStatus; + private String shippingAddress; + private LocalDateTime createdAt; + private LocalDateTime updatedAt; + private List orderItems; +} \ No newline at end of file diff --git a/src/main/java/com/example/project_it207_server/model/dto/response/StatusTimeline.java b/src/main/java/com/example/project_it207_server/model/dto/response/StatusTimeline.java new file mode 100644 index 0000000..082661b --- /dev/null +++ b/src/main/java/com/example/project_it207_server/model/dto/response/StatusTimeline.java @@ -0,0 +1,11 @@ +package com.example.project_it207_server.model.dto.response; + +import lombok.*; +import java.time.LocalDateTime; + +@Getter @Setter @NoArgsConstructor @AllArgsConstructor @Builder +public class StatusTimeline { + private String status; + private LocalDateTime timestamp; + private boolean completed; +} diff --git a/src/main/java/com/example/project_it207_server/model/entity/Order.java b/src/main/java/com/example/project_it207_server/model/entity/Order.java index a571f74..4e61b80 100644 --- a/src/main/java/com/example/project_it207_server/model/entity/Order.java +++ b/src/main/java/com/example/project_it207_server/model/entity/Order.java @@ -21,9 +21,24 @@ public class Order { private BigDecimal totalPrice; private String orderStatus = "PENDING"; private String shippingAddress; - private LocalDateTime createdAt = LocalDateTime.now(); - private LocalDateTime updatedAt = LocalDateTime.now(); - @OneToMany(mappedBy = "order", cascade = CascadeType.ALL) + @Column(name = "created_at", updatable = false) + private LocalDateTime createdAt; + + @Column(name = "updated_at") + private LocalDateTime updatedAt; + + @OneToMany(mappedBy = "order", cascade = CascadeType.ALL, orphanRemoval = true) private List orderItems; + + @PrePersist + protected void onCreate() { + createdAt = LocalDateTime.now(); + updatedAt = LocalDateTime.now(); + } + + @PreUpdate + protected void onUpdate() { + updatedAt = LocalDateTime.now(); + } } \ No newline at end of file diff --git a/src/main/java/com/example/project_it207_server/model/entity/OrderItem.java b/src/main/java/com/example/project_it207_server/model/entity/OrderItem.java index cef2fa5..1c2f70d 100644 --- a/src/main/java/com/example/project_it207_server/model/entity/OrderItem.java +++ b/src/main/java/com/example/project_it207_server/model/entity/OrderItem.java @@ -11,16 +11,16 @@ import java.math.BigDecimal; @NoArgsConstructor @AllArgsConstructor @Builder -class OrderItem { +public class OrderItem { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long orderItemId; - @ManyToOne + @ManyToOne(fetch = FetchType.LAZY) @JoinColumn(name = "order_id") private Order order; - @ManyToOne + @ManyToOne(fetch = FetchType.LAZY) @JoinColumn(name = "product_id") private Product product; diff --git a/src/main/java/com/example/project_it207_server/repository/CartItemRepository.java b/src/main/java/com/example/project_it207_server/repository/CartItemRepository.java index bb202f1..f4284f1 100644 --- a/src/main/java/com/example/project_it207_server/repository/CartItemRepository.java +++ b/src/main/java/com/example/project_it207_server/repository/CartItemRepository.java @@ -21,4 +21,8 @@ public interface CartItemRepository extends JpaRepository { Optional findByCartAndProduct(@Param("cart") Cart cart, @Param("product") Product product); void deleteAllByCart(Cart cart); + + List findByCart_CartId(Long cartId); + + void deleteByCart_CartId(Long cartId); } \ No newline at end of file diff --git a/src/main/java/com/example/project_it207_server/repository/CartRepository.java b/src/main/java/com/example/project_it207_server/repository/CartRepository.java index b15de6e..22e21c0 100644 --- a/src/main/java/com/example/project_it207_server/repository/CartRepository.java +++ b/src/main/java/com/example/project_it207_server/repository/CartRepository.java @@ -1,4 +1,3 @@ -// CartRepository.java package com.example.project_it207_server.repository; import com.example.project_it207_server.model.entity.Cart; @@ -14,11 +13,19 @@ import java.util.List; @Repository public interface CartRepository extends JpaRepository { - // Thay đổi: Sử dụng List thay vì Optional để tránh lỗi + // Lấy tất cả giỏ hàng của user (có thể có nhiều) @Query("SELECT c FROM Cart c WHERE c.user = :user ORDER BY c.createdAt DESC") List findAllByUser(@Param("user") User user); - // Hoặc giữ nguyên nhưng thêm LIMIT 1 + // Lấy giỏ hàng mới nhất của user @Query("SELECT c FROM Cart c WHERE c.user = :user ORDER BY c.createdAt DESC LIMIT 1") Optional findByUser(@Param("user") User user); + + // Tìm giỏ hàng theo userId + @Query("SELECT c FROM Cart c WHERE c.user.userId = :userId ORDER BY c.createdAt DESC LIMIT 1") + Optional findByUserId(@Param("userId") Long userId); + + // Kiểm tra user có giỏ hàng không + @Query("SELECT COUNT(c) > 0 FROM Cart c WHERE c.user.userId = :userId") + boolean existsByUserId(@Param("userId") Long userId); } \ No newline at end of file diff --git a/src/main/java/com/example/project_it207_server/repository/OrderRepository.java b/src/main/java/com/example/project_it207_server/repository/OrderRepository.java index 0e5ccb3..a2cf364 100644 --- a/src/main/java/com/example/project_it207_server/repository/OrderRepository.java +++ b/src/main/java/com/example/project_it207_server/repository/OrderRepository.java @@ -2,5 +2,22 @@ package com.example.project_it207_server.repository; import com.example.project_it207_server.model.entity.Order; import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; -public interface OrderRepository extends JpaRepository {} \ No newline at end of file +import java.util.List; + +@Repository +public interface OrderRepository extends JpaRepository { + + // Lấy tất cả đơn hàng của user, sắp xếp theo ngày tạo giảm dần + List findByUser_UserIdOrderByCreatedAtDesc(Long userId); + + // Lọc đơn hàng theo trạng thái + List findByUser_UserIdAndOrderStatusOrderByCreatedAtDesc(Long userId, String orderStatus); + + // Đếm số đơn hàng theo trạng thái + Long countByUser_UserIdAndOrderStatus(Long userId, String orderStatus); + + // Kiểm tra đơn hàng có tồn tại không + boolean existsByOrderIdAndUser_UserId(Long orderId, Long userId); +} \ No newline at end of file diff --git a/src/main/java/com/example/project_it207_server/service/AuthService.java b/src/main/java/com/example/project_it207_server/service/AuthService.java index 50a7663..47a78cf 100644 --- a/src/main/java/com/example/project_it207_server/service/AuthService.java +++ b/src/main/java/com/example/project_it207_server/service/AuthService.java @@ -76,4 +76,10 @@ public class AuthService { String redisKey = "token:" + username + ":" + token; redisService.deleteKey(redisKey); } + + public Long getUserIdByEmail(String email) { + User user = userRepository.findByEmail(email) + .orElseThrow(() -> new RuntimeException("User not found")); + return user.getUserId(); + } } diff --git a/src/main/java/com/example/project_it207_server/service/CartService.java b/src/main/java/com/example/project_it207_server/service/CartService.java index 9c8b55e..cdeff39 100644 --- a/src/main/java/com/example/project_it207_server/service/CartService.java +++ b/src/main/java/com/example/project_it207_server/service/CartService.java @@ -77,52 +77,37 @@ public class CartService { @Transactional public CartResponse getCartByUserId(Long userId) { - User user = userRepository.findById(userId) - .orElseThrow(() -> new RuntimeException("User not found")); + Cart cart = cartRepository.findByUserId(userId) + .orElseThrow(() -> new RuntimeException("Cart not found")); - // FIX: Xử lý trường hợp có nhiều cart - List carts = cartRepository.findAllByUser(user); - - Cart cart; - if (carts.isEmpty()) { - // Tạo cart mới nếu chưa có - cart = Cart.builder() - .user(user) - .items(new ArrayList<>()) - .createdAt(LocalDateTime.now()) - .build(); - cart = cartRepository.save(cart); - } else { - // Lấy cart mới nhất - cart = carts.get(0); - - // Xóa các cart cũ (nếu có nhiều cart) - for (int i = 1; i < carts.size(); i++) { - Cart oldCart = carts.get(i); - cartItemRepository.deleteAllByCart(oldCart); - cartRepository.delete(oldCart); - } - } - - List items = (cart.getItems() == null ? List.of() : cart.getItems()) - .stream() - .map(this::mapToCartItemResponse) - .collect(Collectors.toList()); + List items = cart.getItems().stream() + .map(item -> CartItemResponse.builder() + .cartItemId(item.getCartItemId()) + .quantity(item.getQuantity()) + .product( + CartItemResponse.ProductInCart.builder() + .productId(item.getProduct().getProductId()) + .productName(item.getProduct().getProductName()) + .imageUrl(item.getProduct().getImageUrl()) + .price(item.getProduct().getPrice()) + .build() + ) + .subtotal(item.getProduct().getPrice() + .multiply(BigDecimal.valueOf(item.getQuantity()))) + .build() + ) + .toList(); BigDecimal totalAmount = items.stream() .map(CartItemResponse::getSubtotal) .reduce(BigDecimal.ZERO, BigDecimal::add); - Integer totalItems = items.stream() - .mapToInt(CartItemResponse::getQuantity) - .sum(); - return CartResponse.builder() .cartId(cart.getCartId()) - .userId(user.getUserId()) + .userId(cart.getUser().getUserId()) .items(items) + .totalItems(items.size()) .totalAmount(totalAmount) - .totalItems(totalItems) .createdAt(cart.getCreatedAt()) .build(); } @@ -188,11 +173,15 @@ public class CartService { return CartItemResponse.builder() .cartItemId(cartItem.getCartItemId()) - .productId(cartItem.getProduct().getProductId()) - .productName(cartItem.getProduct().getProductName()) - .productImage(cartItem.getProduct().getImageUrl()) .quantity(cartItem.getQuantity()) - .price(cartItem.getPrice()) + .product( + CartItemResponse.ProductInCart.builder() + .productId(cartItem.getProduct().getProductId()) + .productName(cartItem.getProduct().getProductName()) + .imageUrl(cartItem.getProduct().getImageUrl()) + .price(cartItem.getProduct().getPrice()) + .build() + ) .subtotal(subtotal) .build(); } diff --git a/src/main/java/com/example/project_it207_server/service/OrderService.java b/src/main/java/com/example/project_it207_server/service/OrderService.java new file mode 100644 index 0000000..ab5c7af --- /dev/null +++ b/src/main/java/com/example/project_it207_server/service/OrderService.java @@ -0,0 +1,193 @@ +package com.example.project_it207_server.service; + +import com.example.project_it207_server.model.dto.request.CreateOrderRequest; +import com.example.project_it207_server.model.dto.response.*; +import com.example.project_it207_server.model.entity.*; +import com.example.project_it207_server.repository.*; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.math.BigDecimal; +import java.time.LocalDateTime; +import java.util.*; +import java.util.stream.Collectors; + +@Service +@RequiredArgsConstructor +public class OrderService { + + private final OrderRepository orderRepository; + private final ProductRepository productRepository; + private final UserRepository userRepository; + private final CartRepository cartRepository; + private final CartItemRepository cartItemRepository; + + @Transactional + public OrderResponse createOrder(Long userId, CreateOrderRequest request) { + User user = userRepository.findById(userId) + .orElseThrow(() -> new RuntimeException("User not found")); + + Order order = Order.builder() + .user(user) + .shippingAddress(request.getShippingAddress()) + .orderStatus("PENDING") + .totalPrice(BigDecimal.ZERO) + .createdAt(LocalDateTime.now()) + .updatedAt(LocalDateTime.now()) + .build(); + + List orderItems = new ArrayList<>(); + BigDecimal totalPrice = BigDecimal.ZERO; + + for (var itemReq : request.getItems()) { + Product product = productRepository.findById(itemReq.getProductId()) + .orElseThrow(() -> new RuntimeException("Product not found: " + itemReq.getProductId())); + + BigDecimal itemPrice = product.getPrice().multiply(new BigDecimal(itemReq.getQuantity())); + totalPrice = totalPrice.add(itemPrice); + + OrderItem orderItem = OrderItem.builder() + .order(order) + .product(product) + .quantity(itemReq.getQuantity()) + .price(product.getPrice()) + .build(); + + orderItems.add(orderItem); + } + + order.setTotalPrice(totalPrice); + order.setOrderItems(orderItems); + + Order savedOrder = orderRepository.save(order); + + // Clear cart after creating order + Optional cartOpt = cartRepository.findByUser(user); + if (cartOpt.isPresent()) { + Cart cart = cartOpt.get(); + if (cart.getItems() != null && !cart.getItems().isEmpty()) { + cartItemRepository.deleteAll(cart.getItems()); + } + } + + return mapToOrderResponse(savedOrder); + } + + public List getAllOrders(Long userId) { + List orders = orderRepository.findByUser_UserIdOrderByCreatedAtDesc(userId); + return orders.stream() + .map(this::mapToOrderResponse) + .collect(Collectors.toList()); + } + + public List getOrdersByStatus(Long userId, String status) { + List orders = orderRepository.findByUser_UserIdAndOrderStatusOrderByCreatedAtDesc(userId, status); + return orders.stream() + .map(this::mapToOrderResponse) + .collect(Collectors.toList()); + } + + public OrderResponse getOrderDetail(Long orderId, Long userId) { + Order order = orderRepository.findById(orderId) + .orElseThrow(() -> new RuntimeException("Order not found")); + + if (!order.getUser().getUserId().equals(userId)) { + throw new RuntimeException("Unauthorized access to order"); + } + + return mapToOrderResponse(order); + } + + public OrderProgressResponse getOrderProgress(Long orderId, Long userId) { + Order order = orderRepository.findById(orderId) + .orElseThrow(() -> new RuntimeException("Order not found")); + + if (!order.getUser().getUserId().equals(userId)) { + throw new RuntimeException("Unauthorized access to order"); + } + + List timeline = Arrays.asList( + StatusTimeline.builder() + .status("PENDING") + .timestamp(order.getCreatedAt()) + .completed(true) + .build(), + StatusTimeline.builder() + .status("CONFIRMED") + .timestamp(order.getOrderStatus().equals("CONFIRMED") || + order.getOrderStatus().equals("SHIPPING") || + order.getOrderStatus().equals("DELIVERED") ? + order.getUpdatedAt() : null) + .completed(order.getOrderStatus().equals("CONFIRMED") || + order.getOrderStatus().equals("SHIPPING") || + order.getOrderStatus().equals("DELIVERED")) + .build(), + StatusTimeline.builder() + .status("SHIPPING") + .timestamp(order.getOrderStatus().equals("SHIPPING") || + order.getOrderStatus().equals("DELIVERED") ? + order.getUpdatedAt() : null) + .completed(order.getOrderStatus().equals("SHIPPING") || + order.getOrderStatus().equals("DELIVERED")) + .build(), + StatusTimeline.builder() + .status("DELIVERED") + .timestamp(order.getOrderStatus().equals("DELIVERED") ? + order.getUpdatedAt() : null) + .completed(order.getOrderStatus().equals("DELIVERED")) + .build() + ); + + return OrderProgressResponse.builder() + .orderId(order.getOrderId()) + .orderStatus(order.getOrderStatus()) + .timeline(timeline) + .build(); + } + + @Transactional + public OrderResponse cancelOrder(Long orderId, Long userId) { + Order order = orderRepository.findById(orderId) + .orElseThrow(() -> new RuntimeException("Order not found")); + + if (!order.getUser().getUserId().equals(userId)) { + throw new RuntimeException("Unauthorized access to order"); + } + + if (order.getOrderStatus().equals("DELIVERED") || order.getOrderStatus().equals("CANCELLED")) { + throw new RuntimeException("Cannot cancel order with status: " + order.getOrderStatus()); + } + + order.setOrderStatus("CANCELLED"); + order.setUpdatedAt(LocalDateTime.now()); + + Order updatedOrder = orderRepository.save(order); + return mapToOrderResponse(updatedOrder); + } + + private OrderResponse mapToOrderResponse(Order order) { + List itemResponses = order.getOrderItems().stream() + .map(item -> OrderItemResponse.builder() + .orderItemId(item.getOrderItemId()) + .productId(item.getProduct().getProductId()) + .productName(item.getProduct().getProductName()) + .quantity(item.getQuantity()) + .price(item.getPrice()) + .subtotal(item.getPrice().multiply(new BigDecimal(item.getQuantity()))) + .build()) + .collect(Collectors.toList()); + + return OrderResponse.builder() + .orderId(order.getOrderId()) + .userId(order.getUser().getUserId()) + .userName(order.getUser().getLastName()) + .totalPrice(order.getTotalPrice()) + .orderStatus(order.getOrderStatus()) + .shippingAddress(order.getShippingAddress()) + .createdAt(order.getCreatedAt()) + .updatedAt(order.getUpdatedAt()) + .orderItems(itemResponses) + .build(); + } +} \ No newline at end of file