order
This commit is contained in:
@@ -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<Map<String, Object>> createOrder(
|
||||||
|
@PathVariable Long userId,
|
||||||
|
@RequestBody CreateOrderRequest request) {
|
||||||
|
try {
|
||||||
|
OrderResponse order = orderService.createOrder(userId, request);
|
||||||
|
|
||||||
|
Map<String, Object> 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<String, Object> 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<Map<String, Object>> getAllOrders(Authentication authentication) {
|
||||||
|
try {
|
||||||
|
// Lấy email từ token
|
||||||
|
String email = authentication.getName();
|
||||||
|
|
||||||
|
// Lấy userId từ email
|
||||||
|
Long userId = authService.getUserIdByEmail(email);
|
||||||
|
List<OrderResponse> orders = orderService.getAllOrders(userId);
|
||||||
|
|
||||||
|
Map<String, Object> response = new HashMap<>();
|
||||||
|
response.put("success", true);
|
||||||
|
response.put("data", orders);
|
||||||
|
response.put("total", orders.size());
|
||||||
|
|
||||||
|
return ResponseEntity.ok(response);
|
||||||
|
} catch (Exception e) {
|
||||||
|
Map<String, Object> 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<Map<String, Object>> 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<OrderResponse> orders = orderService.getOrdersByStatus(userId, status.toUpperCase());
|
||||||
|
|
||||||
|
Map<String, Object> 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<String, Object> 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<Map<String, Object>> 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<String, Object> response = new HashMap<>();
|
||||||
|
response.put("success", true);
|
||||||
|
response.put("data", order);
|
||||||
|
|
||||||
|
return ResponseEntity.ok(response);
|
||||||
|
} catch (Exception e) {
|
||||||
|
Map<String, Object> 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<Map<String, Object>> 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<String, Object> response = new HashMap<>();
|
||||||
|
response.put("success", true);
|
||||||
|
response.put("data", progress);
|
||||||
|
|
||||||
|
return ResponseEntity.ok(response);
|
||||||
|
} catch (Exception e) {
|
||||||
|
Map<String, Object> 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<Map<String, Object>> 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<String, Object> 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<String, Object> response = new HashMap<>();
|
||||||
|
response.put("success", false);
|
||||||
|
response.put("message", e.getMessage());
|
||||||
|
return ResponseEntity.badRequest().body(response);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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<OrderItemRequest> items;
|
||||||
|
}
|
||||||
@@ -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;
|
||||||
|
}
|
||||||
@@ -10,10 +10,22 @@ import java.math.BigDecimal;
|
|||||||
@Builder
|
@Builder
|
||||||
public class CartItemResponse {
|
public class CartItemResponse {
|
||||||
private Long cartItemId;
|
private Long cartItemId;
|
||||||
|
private Integer quantity;
|
||||||
|
|
||||||
|
// 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 Long productId;
|
||||||
private String productName;
|
private String productName;
|
||||||
private String productImage;
|
private String imageUrl;
|
||||||
private Integer quantity;
|
|
||||||
private BigDecimal price;
|
private BigDecimal price;
|
||||||
private BigDecimal subtotal; // price * quantity
|
}
|
||||||
|
|
||||||
|
private BigDecimal subtotal;
|
||||||
}
|
}
|
||||||
@@ -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;
|
||||||
|
}
|
||||||
@@ -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<StatusTimeline> timeline;
|
||||||
|
}
|
||||||
@@ -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<OrderItemResponse> orderItems;
|
||||||
|
}
|
||||||
@@ -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;
|
||||||
|
}
|
||||||
@@ -21,9 +21,24 @@ public class Order {
|
|||||||
private BigDecimal totalPrice;
|
private BigDecimal totalPrice;
|
||||||
private String orderStatus = "PENDING";
|
private String orderStatus = "PENDING";
|
||||||
private String shippingAddress;
|
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<OrderItem> orderItems;
|
private List<OrderItem> orderItems;
|
||||||
|
|
||||||
|
@PrePersist
|
||||||
|
protected void onCreate() {
|
||||||
|
createdAt = LocalDateTime.now();
|
||||||
|
updatedAt = LocalDateTime.now();
|
||||||
|
}
|
||||||
|
|
||||||
|
@PreUpdate
|
||||||
|
protected void onUpdate() {
|
||||||
|
updatedAt = LocalDateTime.now();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -11,16 +11,16 @@ import java.math.BigDecimal;
|
|||||||
@NoArgsConstructor
|
@NoArgsConstructor
|
||||||
@AllArgsConstructor
|
@AllArgsConstructor
|
||||||
@Builder
|
@Builder
|
||||||
class OrderItem {
|
public class OrderItem {
|
||||||
@Id
|
@Id
|
||||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||||
private Long orderItemId;
|
private Long orderItemId;
|
||||||
|
|
||||||
@ManyToOne
|
@ManyToOne(fetch = FetchType.LAZY)
|
||||||
@JoinColumn(name = "order_id")
|
@JoinColumn(name = "order_id")
|
||||||
private Order order;
|
private Order order;
|
||||||
|
|
||||||
@ManyToOne
|
@ManyToOne(fetch = FetchType.LAZY)
|
||||||
@JoinColumn(name = "product_id")
|
@JoinColumn(name = "product_id")
|
||||||
private Product product;
|
private Product product;
|
||||||
|
|
||||||
|
|||||||
@@ -21,4 +21,8 @@ public interface CartItemRepository extends JpaRepository<CartItem, Long> {
|
|||||||
Optional<CartItem> findByCartAndProduct(@Param("cart") Cart cart, @Param("product") Product product);
|
Optional<CartItem> findByCartAndProduct(@Param("cart") Cart cart, @Param("product") Product product);
|
||||||
|
|
||||||
void deleteAllByCart(Cart cart);
|
void deleteAllByCart(Cart cart);
|
||||||
|
|
||||||
|
List<CartItem> findByCart_CartId(Long cartId);
|
||||||
|
|
||||||
|
void deleteByCart_CartId(Long cartId);
|
||||||
}
|
}
|
||||||
@@ -1,4 +1,3 @@
|
|||||||
// CartRepository.java
|
|
||||||
package com.example.project_it207_server.repository;
|
package com.example.project_it207_server.repository;
|
||||||
|
|
||||||
import com.example.project_it207_server.model.entity.Cart;
|
import com.example.project_it207_server.model.entity.Cart;
|
||||||
@@ -14,11 +13,19 @@ import java.util.List;
|
|||||||
@Repository
|
@Repository
|
||||||
public interface CartRepository extends JpaRepository<Cart, Long> {
|
public interface CartRepository extends JpaRepository<Cart, Long> {
|
||||||
|
|
||||||
// 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")
|
@Query("SELECT c FROM Cart c WHERE c.user = :user ORDER BY c.createdAt DESC")
|
||||||
List<Cart> findAllByUser(@Param("user") User user);
|
List<Cart> 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")
|
@Query("SELECT c FROM Cart c WHERE c.user = :user ORDER BY c.createdAt DESC LIMIT 1")
|
||||||
Optional<Cart> findByUser(@Param("user") User user);
|
Optional<Cart> 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<Cart> 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);
|
||||||
}
|
}
|
||||||
@@ -2,5 +2,22 @@ package com.example.project_it207_server.repository;
|
|||||||
|
|
||||||
import com.example.project_it207_server.model.entity.Order;
|
import com.example.project_it207_server.model.entity.Order;
|
||||||
import org.springframework.data.jpa.repository.JpaRepository;
|
import org.springframework.data.jpa.repository.JpaRepository;
|
||||||
|
import org.springframework.stereotype.Repository;
|
||||||
|
|
||||||
public interface OrderRepository extends JpaRepository<Order, Long> {}
|
import java.util.List;
|
||||||
|
|
||||||
|
@Repository
|
||||||
|
public interface OrderRepository extends JpaRepository<Order, Long> {
|
||||||
|
|
||||||
|
// 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<Order> findByUser_UserIdOrderByCreatedAtDesc(Long userId);
|
||||||
|
|
||||||
|
// Lọc đơn hàng theo trạng thái
|
||||||
|
List<Order> 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);
|
||||||
|
}
|
||||||
@@ -76,4 +76,10 @@ public class AuthService {
|
|||||||
String redisKey = "token:" + username + ":" + token;
|
String redisKey = "token:" + username + ":" + token;
|
||||||
redisService.deleteKey(redisKey);
|
redisService.deleteKey(redisKey);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Long getUserIdByEmail(String email) {
|
||||||
|
User user = userRepository.findByEmail(email)
|
||||||
|
.orElseThrow(() -> new RuntimeException("User not found"));
|
||||||
|
return user.getUserId();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -77,52 +77,37 @@ public class CartService {
|
|||||||
|
|
||||||
@Transactional
|
@Transactional
|
||||||
public CartResponse getCartByUserId(Long userId) {
|
public CartResponse getCartByUserId(Long userId) {
|
||||||
User user = userRepository.findById(userId)
|
Cart cart = cartRepository.findByUserId(userId)
|
||||||
.orElseThrow(() -> new RuntimeException("User not found"));
|
.orElseThrow(() -> new RuntimeException("Cart not found"));
|
||||||
|
|
||||||
// FIX: Xử lý trường hợp có nhiều cart
|
List<CartItemResponse> items = cart.getItems().stream()
|
||||||
List<Cart> carts = cartRepository.findAllByUser(user);
|
.map(item -> CartItemResponse.builder()
|
||||||
|
.cartItemId(item.getCartItemId())
|
||||||
Cart cart;
|
.quantity(item.getQuantity())
|
||||||
if (carts.isEmpty()) {
|
.product(
|
||||||
// Tạo cart mới nếu chưa có
|
CartItemResponse.ProductInCart.builder()
|
||||||
cart = Cart.builder()
|
.productId(item.getProduct().getProductId())
|
||||||
.user(user)
|
.productName(item.getProduct().getProductName())
|
||||||
.items(new ArrayList<>())
|
.imageUrl(item.getProduct().getImageUrl())
|
||||||
.createdAt(LocalDateTime.now())
|
.price(item.getProduct().getPrice())
|
||||||
.build();
|
.build()
|
||||||
cart = cartRepository.save(cart);
|
)
|
||||||
} else {
|
.subtotal(item.getProduct().getPrice()
|
||||||
// Lấy cart mới nhất
|
.multiply(BigDecimal.valueOf(item.getQuantity())))
|
||||||
cart = carts.get(0);
|
.build()
|
||||||
|
)
|
||||||
// Xóa các cart cũ (nếu có nhiều cart)
|
.toList();
|
||||||
for (int i = 1; i < carts.size(); i++) {
|
|
||||||
Cart oldCart = carts.get(i);
|
|
||||||
cartItemRepository.deleteAllByCart(oldCart);
|
|
||||||
cartRepository.delete(oldCart);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
List<CartItemResponse> items = (cart.getItems() == null ? List.<CartItem>of() : cart.getItems())
|
|
||||||
.stream()
|
|
||||||
.map(this::mapToCartItemResponse)
|
|
||||||
.collect(Collectors.toList());
|
|
||||||
|
|
||||||
BigDecimal totalAmount = items.stream()
|
BigDecimal totalAmount = items.stream()
|
||||||
.map(CartItemResponse::getSubtotal)
|
.map(CartItemResponse::getSubtotal)
|
||||||
.reduce(BigDecimal.ZERO, BigDecimal::add);
|
.reduce(BigDecimal.ZERO, BigDecimal::add);
|
||||||
|
|
||||||
Integer totalItems = items.stream()
|
|
||||||
.mapToInt(CartItemResponse::getQuantity)
|
|
||||||
.sum();
|
|
||||||
|
|
||||||
return CartResponse.builder()
|
return CartResponse.builder()
|
||||||
.cartId(cart.getCartId())
|
.cartId(cart.getCartId())
|
||||||
.userId(user.getUserId())
|
.userId(cart.getUser().getUserId())
|
||||||
.items(items)
|
.items(items)
|
||||||
|
.totalItems(items.size())
|
||||||
.totalAmount(totalAmount)
|
.totalAmount(totalAmount)
|
||||||
.totalItems(totalItems)
|
|
||||||
.createdAt(cart.getCreatedAt())
|
.createdAt(cart.getCreatedAt())
|
||||||
.build();
|
.build();
|
||||||
}
|
}
|
||||||
@@ -188,11 +173,15 @@ public class CartService {
|
|||||||
|
|
||||||
return CartItemResponse.builder()
|
return CartItemResponse.builder()
|
||||||
.cartItemId(cartItem.getCartItemId())
|
.cartItemId(cartItem.getCartItemId())
|
||||||
|
.quantity(cartItem.getQuantity())
|
||||||
|
.product(
|
||||||
|
CartItemResponse.ProductInCart.builder()
|
||||||
.productId(cartItem.getProduct().getProductId())
|
.productId(cartItem.getProduct().getProductId())
|
||||||
.productName(cartItem.getProduct().getProductName())
|
.productName(cartItem.getProduct().getProductName())
|
||||||
.productImage(cartItem.getProduct().getImageUrl())
|
.imageUrl(cartItem.getProduct().getImageUrl())
|
||||||
.quantity(cartItem.getQuantity())
|
.price(cartItem.getProduct().getPrice())
|
||||||
.price(cartItem.getPrice())
|
.build()
|
||||||
|
)
|
||||||
.subtotal(subtotal)
|
.subtotal(subtotal)
|
||||||
.build();
|
.build();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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<OrderItem> 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<Cart> 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<OrderResponse> getAllOrders(Long userId) {
|
||||||
|
List<Order> orders = orderRepository.findByUser_UserIdOrderByCreatedAtDesc(userId);
|
||||||
|
return orders.stream()
|
||||||
|
.map(this::mapToOrderResponse)
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<OrderResponse> getOrdersByStatus(Long userId, String status) {
|
||||||
|
List<Order> 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<StatusTimeline> 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<OrderItemResponse> 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();
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user