From 7c9dad3f2a32f3d192f1866d37031fc213b8015e Mon Sep 17 00:00:00 2001 From: IlonaChupa Date: Sun, 31 May 2026 17:30:39 +0300 Subject: [PATCH 1/4] add Order support --- .../controller/OrderController.java | 84 ++++++++++++ .../dto/CreateOrderRequestDto.java | 12 ++ .../spring/springbootintro/dto/OrderDto.java | 19 +++ .../springbootintro/dto/OrderItemDto.java | 10 ++ .../dto/UpdateOrderStatusRequestDto.java | 13 ++ .../mapper/OrderItemMapper.java | 18 +++ .../springbootintro/mapper/OrderMapper.java | 14 ++ .../spring/springbootintro/model/Order.java | 53 ++++++++ .../springbootintro/model/OrderItem.java | 43 ++++++ .../spring/springbootintro/model/Status.java | 8 ++ .../repository/OrderItemRepository.java | 9 ++ .../repository/OrderRepository.java | 14 ++ .../springbootintro/service/OrderService.java | 22 +++ .../service/impl/OrderServiceImpl.java | 126 ++++++++++++++++++ .../07-\321\201reate-orders-table.yaml" | 3 + 15 files changed, 448 insertions(+) create mode 100644 src/main/java/spring/springbootintro/controller/OrderController.java create mode 100644 src/main/java/spring/springbootintro/dto/CreateOrderRequestDto.java create mode 100644 src/main/java/spring/springbootintro/dto/OrderDto.java create mode 100644 src/main/java/spring/springbootintro/dto/OrderItemDto.java create mode 100644 src/main/java/spring/springbootintro/dto/UpdateOrderStatusRequestDto.java create mode 100644 src/main/java/spring/springbootintro/mapper/OrderItemMapper.java create mode 100644 src/main/java/spring/springbootintro/mapper/OrderMapper.java create mode 100644 src/main/java/spring/springbootintro/model/Order.java create mode 100644 src/main/java/spring/springbootintro/model/OrderItem.java create mode 100644 src/main/java/spring/springbootintro/model/Status.java create mode 100644 src/main/java/spring/springbootintro/repository/OrderItemRepository.java create mode 100644 src/main/java/spring/springbootintro/repository/OrderRepository.java create mode 100644 src/main/java/spring/springbootintro/service/OrderService.java create mode 100644 src/main/java/spring/springbootintro/service/impl/OrderServiceImpl.java create mode 100644 "src/main/resources/db/changelog/changes/07-\321\201reate-orders-table.yaml" diff --git a/src/main/java/spring/springbootintro/controller/OrderController.java b/src/main/java/spring/springbootintro/controller/OrderController.java new file mode 100644 index 0000000..3a2f99b --- /dev/null +++ b/src/main/java/spring/springbootintro/controller/OrderController.java @@ -0,0 +1,84 @@ +package spring.springbootintro.controller; + +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.validation.Valid; +import java.util.List; +import lombok.RequiredArgsConstructor; +import org.springframework.data.domain.Pageable; +import org.springframework.http.HttpStatus; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.security.core.Authentication; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PatchMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.ResponseStatus; +import org.springframework.web.bind.annotation.RestController; +import spring.springbootintro.dto.CreateOrderRequestDto; +import spring.springbootintro.dto.OrderDto; +import spring.springbootintro.dto.OrderItemDto; +import spring.springbootintro.dto.UpdateOrderStatusRequestDto; +import spring.springbootintro.model.User; +import spring.springbootintro.service.OrderService; + +@Tag(name = "Order management", description = "Endpoints for managing user orders") +@RestController +@RequiredArgsConstructor +@RequestMapping("/orders") +public class OrderController { + + private final OrderService orderService; + + @PostMapping + @ResponseStatus(HttpStatus.CREATED) + @PreAuthorize("hasRole('USER')") + @Operation(summary = "Create an order", + description = "Creates a new order from the user's current shopping cart and clears the cart.") + public OrderDto createOrder(Authentication authentication, + @RequestBody @Valid CreateOrderRequestDto requestDto) { + User user = (User) authentication.getPrincipal(); + return orderService.createOrder(user.getId(), requestDto); + } + + @GetMapping + @PreAuthorize("hasRole('USER')") + @Operation(summary = "Get order history", + description = "Retrieves the order history for the currently authenticated user.") + public List getOrderHistory(Authentication authentication, Pageable pageable) { + User user = (User) authentication.getPrincipal(); + return orderService.getOrderHistory(user.getId(), pageable); + } + + @PatchMapping("/{id}") + @PreAuthorize("hasRole('ADMIN')") + @Operation(summary = "Update order status", + description = "Updates the status of an existing order. Available only for admins.") + public OrderDto updateOrderStatus(@PathVariable Long id, + @RequestBody @Valid UpdateOrderStatusRequestDto requestDto) { + return orderService.updateOrderStatus(id, requestDto); + } + + @GetMapping("/{orderId}/items") + @PreAuthorize("hasRole('USER')") + @Operation(summary = "Get all items from an order", + description = "Retrieves all OrderItems for a specific order belonging to the user.") + public List getOrderItems(Authentication authentication, + @PathVariable Long orderId) { + User user = (User) authentication.getPrincipal(); + return orderService.getOrderItems(user.getId(), orderId); + } + + @GetMapping("/{orderId}/items/{itemId}") + @PreAuthorize("hasRole('USER')") + @Operation(summary = "Get a specific item from an order", + description = "Retrieves a specific OrderItem by its ID within a specific order.") + public OrderItemDto getOrderItem(Authentication authentication, + @PathVariable Long orderId, + @PathVariable Long itemId) { + User user = (User) authentication.getPrincipal(); + return orderService.getOrderItem(user.getId(), orderId, itemId); + } +} diff --git a/src/main/java/spring/springbootintro/dto/CreateOrderRequestDto.java b/src/main/java/spring/springbootintro/dto/CreateOrderRequestDto.java new file mode 100644 index 0000000..5363de4 --- /dev/null +++ b/src/main/java/spring/springbootintro/dto/CreateOrderRequestDto.java @@ -0,0 +1,12 @@ +package spring.springbootintro.dto; + +import jakarta.validation.constraints.NotBlank; +import lombok.Getter; +import lombok.Setter; + +@Getter +@Setter +public class CreateOrderRequestDto { + @NotBlank + private String shippingAddress; +} diff --git a/src/main/java/spring/springbootintro/dto/OrderDto.java b/src/main/java/spring/springbootintro/dto/OrderDto.java new file mode 100644 index 0000000..b1cd024 --- /dev/null +++ b/src/main/java/spring/springbootintro/dto/OrderDto.java @@ -0,0 +1,19 @@ +package spring.springbootintro.dto; + +import java.math.BigDecimal; +import java.time.LocalDateTime; +import java.util.Set; +import lombok.Data; +import spring.springbootintro.model.OrderItem; +import spring.springbootintro.model.Status; + +@Data +public class OrderDto { + private Long id; + private Long userId; + private Set orderItems; + private LocalDateTime orderDate; + private BigDecimal total; + private Status status; + +} diff --git a/src/main/java/spring/springbootintro/dto/OrderItemDto.java b/src/main/java/spring/springbootintro/dto/OrderItemDto.java new file mode 100644 index 0000000..d7559d3 --- /dev/null +++ b/src/main/java/spring/springbootintro/dto/OrderItemDto.java @@ -0,0 +1,10 @@ +package spring.springbootintro.dto; + +import lombok.Data; + +@Data +public class OrderItemDto { + private Long id; + private Long bookId; + private int quantity; +} diff --git a/src/main/java/spring/springbootintro/dto/UpdateOrderStatusRequestDto.java b/src/main/java/spring/springbootintro/dto/UpdateOrderStatusRequestDto.java new file mode 100644 index 0000000..29368b2 --- /dev/null +++ b/src/main/java/spring/springbootintro/dto/UpdateOrderStatusRequestDto.java @@ -0,0 +1,13 @@ +package spring.springbootintro.dto; + +import jakarta.validation.constraints.NotBlank; +import lombok.Getter; +import lombok.Setter; +import spring.springbootintro.model.Status; + +@Getter +@Setter +public class UpdateOrderStatusRequestDto { + @NotBlank + private Status status; +} diff --git a/src/main/java/spring/springbootintro/mapper/OrderItemMapper.java b/src/main/java/spring/springbootintro/mapper/OrderItemMapper.java new file mode 100644 index 0000000..71438e7 --- /dev/null +++ b/src/main/java/spring/springbootintro/mapper/OrderItemMapper.java @@ -0,0 +1,18 @@ +package spring.springbootintro.mapper; + +import org.mapstruct.Mapper; +import org.mapstruct.Mapping; +import spring.springbootintro.dto.OrderItemDto; +import spring.springbootintro.model.OrderItem; + +@Mapper(config = spring.springbootintro.config.MapperConfig.class) +public interface OrderItemMapper { + + @Mapping(source = "book.id", target = "bookId") + OrderItemDto toDto(OrderItem orderItem); + + @Mapping(target = "id", ignore = true) + @Mapping(target = "deleted", ignore = true) + OrderItem toModel(OrderItemDto orderItemDto); + +} diff --git a/src/main/java/spring/springbootintro/mapper/OrderMapper.java b/src/main/java/spring/springbootintro/mapper/OrderMapper.java new file mode 100644 index 0000000..cb38e55 --- /dev/null +++ b/src/main/java/spring/springbootintro/mapper/OrderMapper.java @@ -0,0 +1,14 @@ +package spring.springbootintro.mapper; + +import org.mapstruct.Mapper; +import org.mapstruct.Mapping; +import spring.springbootintro.dto.OrderDto; +import spring.springbootintro.model.Order; + +@Mapper(config = spring.springbootintro.config.MapperConfig.class, uses = OrderItemMapper.class) +public interface OrderMapper { + + @Mapping(source = "user.id", target = "userId") + OrderDto toDto(Order order); + +} diff --git a/src/main/java/spring/springbootintro/model/Order.java b/src/main/java/spring/springbootintro/model/Order.java new file mode 100644 index 0000000..4a49409 --- /dev/null +++ b/src/main/java/spring/springbootintro/model/Order.java @@ -0,0 +1,53 @@ +package spring.springbootintro.model; + +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.GenerationType; +import jakarta.persistence.Id; +import jakarta.persistence.JoinColumn; +import jakarta.persistence.ManyToOne; +import jakarta.persistence.OneToMany; +import jakarta.persistence.Table; +import java.math.BigDecimal; +import java.time.LocalDateTime; +import java.util.Set; +import lombok.Getter; +import lombok.Setter; +import org.hibernate.annotations.SQLDelete; +import org.hibernate.annotations.SQLRestriction; + +@SQLDelete(sql = "UPDATE orders SET is_deleted = true WHERE id = ?") +@SQLRestriction("is_deleted = false") +@Entity +@Table(name = "orders") +@Getter +@Setter +public class Order { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + + @ManyToOne + @JoinColumn(name = "user_id", nullable = false) + private User user; + + @Column(nullable = false) + private Status status; + + @Column(nullable = false) + private BigDecimal total; + + @Column(nullable = false) + private LocalDateTime orderDate; + + @Column(nullable = false) + private String shippingAddress; + + @OneToMany + private Set orderItems; + + private boolean isDeleted = false; + +} diff --git a/src/main/java/spring/springbootintro/model/OrderItem.java b/src/main/java/spring/springbootintro/model/OrderItem.java new file mode 100644 index 0000000..51a0b8d --- /dev/null +++ b/src/main/java/spring/springbootintro/model/OrderItem.java @@ -0,0 +1,43 @@ +package spring.springbootintro.model; + +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.GenerationType; +import jakarta.persistence.Id; +import jakarta.persistence.JoinColumn; +import jakarta.persistence.ManyToOne; +import jakarta.persistence.Table; +import java.math.BigDecimal; +import lombok.Getter; +import lombok.Setter; +import org.hibernate.annotations.SQLDelete; +import org.hibernate.annotations.SQLRestriction; + +@SQLDelete(sql = "UPDATE order_items SET is_deleted = true WHERE id = ?") +@SQLRestriction("is_deleted = false") +@Entity +@Table(name = "order_items") +@Getter +@Setter +public class OrderItem { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + + @ManyToOne + @JoinColumn(name = "order_id") + private Order order; + + @ManyToOne + @JoinColumn(name = "book_id") + private Book book; + + private int quantity; + + @Column(nullable = false) + private BigDecimal price; + + private boolean isDeleted = false; +} diff --git a/src/main/java/spring/springbootintro/model/Status.java b/src/main/java/spring/springbootintro/model/Status.java new file mode 100644 index 0000000..5e02189 --- /dev/null +++ b/src/main/java/spring/springbootintro/model/Status.java @@ -0,0 +1,8 @@ +package spring.springbootintro.model; + +public enum Status { + COMPLETED, + PENDING, + DELIVERED, + CANCELLED +} diff --git a/src/main/java/spring/springbootintro/repository/OrderItemRepository.java b/src/main/java/spring/springbootintro/repository/OrderItemRepository.java new file mode 100644 index 0000000..f9daba0 --- /dev/null +++ b/src/main/java/spring/springbootintro/repository/OrderItemRepository.java @@ -0,0 +1,9 @@ +package spring.springbootintro.repository; + +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; +import spring.springbootintro.model.OrderItem; + +@Repository +public interface OrderItemRepository extends JpaRepository { +} diff --git a/src/main/java/spring/springbootintro/repository/OrderRepository.java b/src/main/java/spring/springbootintro/repository/OrderRepository.java new file mode 100644 index 0000000..a9f3fe4 --- /dev/null +++ b/src/main/java/spring/springbootintro/repository/OrderRepository.java @@ -0,0 +1,14 @@ +package spring.springbootintro.repository; + +import java.util.List; +import org.springframework.data.domain.Pageable; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; +import spring.springbootintro.model.Order; + +@Repository +public interface OrderRepository extends JpaRepository { + + List findAllByUserId(Long userId, Pageable pageable); + +} diff --git a/src/main/java/spring/springbootintro/service/OrderService.java b/src/main/java/spring/springbootintro/service/OrderService.java new file mode 100644 index 0000000..e35ca1a --- /dev/null +++ b/src/main/java/spring/springbootintro/service/OrderService.java @@ -0,0 +1,22 @@ +package spring.springbootintro.service; + +import java.util.List; +import org.springframework.data.domain.Pageable; +import spring.springbootintro.dto.CreateOrderRequestDto; +import spring.springbootintro.dto.OrderDto; +import spring.springbootintro.dto.OrderItemDto; +import spring.springbootintro.dto.UpdateOrderStatusRequestDto; + +public interface OrderService { + + List getOrderHistory(Long userId, Pageable pageable); + + OrderDto createOrder(Long userId, CreateOrderRequestDto requestDto); + + OrderDto updateOrderStatus(Long orderId, UpdateOrderStatusRequestDto orderDto); + + List getOrderItems(Long userId, Long orderId); + + OrderItemDto getOrderItem(Long userId, Long orderId, Long itemId); + +} diff --git a/src/main/java/spring/springbootintro/service/impl/OrderServiceImpl.java b/src/main/java/spring/springbootintro/service/impl/OrderServiceImpl.java new file mode 100644 index 0000000..b6614a0 --- /dev/null +++ b/src/main/java/spring/springbootintro/service/impl/OrderServiceImpl.java @@ -0,0 +1,126 @@ +package spring.springbootintro.service.impl; + +import java.math.BigDecimal; +import java.time.LocalDateTime; +import java.util.HashSet; +import java.util.List; +import java.util.Set; +import lombok.RequiredArgsConstructor; +import org.springframework.data.domain.Pageable; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import spring.springbootintro.dto.CreateOrderRequestDto; +import spring.springbootintro.dto.OrderDto; +import spring.springbootintro.dto.OrderItemDto; +import spring.springbootintro.dto.UpdateOrderStatusRequestDto; +import spring.springbootintro.exception.EntityNotFoundException; +import spring.springbootintro.mapper.OrderItemMapper; +import spring.springbootintro.mapper.OrderMapper; +import spring.springbootintro.model.CartItem; +import spring.springbootintro.model.Order; +import spring.springbootintro.model.OrderItem; +import spring.springbootintro.model.ShoppingCart; +import spring.springbootintro.model.Status; +import spring.springbootintro.repository.OrderRepository; +import spring.springbootintro.repository.ShoppingCartRepository; +import spring.springbootintro.service.OrderService; + +@RequiredArgsConstructor +@Service +public class OrderServiceImpl implements OrderService { + + private final OrderRepository orderRepository; + private final ShoppingCartRepository shoppingCartRepository; + private final OrderMapper orderMapper; + private final OrderItemMapper orderItemMapper; + + @Override + public List getOrderHistory(Long userId, Pageable pageable) { + return orderRepository.findAllByUserId(userId, pageable).stream() + .map(orderMapper::toDto) + .toList(); + } + + @Override + @Transactional + public OrderDto createOrder(Long userId, CreateOrderRequestDto requestDto) { + ShoppingCart shoppingCart = shoppingCartRepository.findByUserId(userId) + .orElseThrow(() -> new EntityNotFoundException("Can't find" + + " shopping cart for user with id: " + userId)); + + if (shoppingCart.getCartItems().isEmpty()) { + throw new IllegalArgumentException("Can't place order with empty shopping cart"); + } + + Order order = new Order(); + order.setUser(shoppingCart.getUser()); + order.setStatus(Status.PENDING); + order.setOrderDate(LocalDateTime.now()); + order.setShippingAddress(requestDto.getShippingAddress()); + + Set orderItems = new HashSet<>(); + BigDecimal total = BigDecimal.ZERO; + + for (CartItem cartItem : shoppingCart.getCartItems()) { + OrderItem orderItem = new OrderItem(); + orderItem.setOrder(order); + orderItem.setBook(cartItem.getBook()); + orderItem.setQuantity(cartItem.getQuantity()); + orderItem.setPrice(cartItem.getBook().getPrice()); + + orderItems.add(orderItem); + + BigDecimal itemTotal = orderItem.getPrice() + .multiply(BigDecimal.valueOf(orderItem.getQuantity())); + total = total.add(itemTotal); + } + + order.setOrderItems(orderItems); + order.setTotal(total); + + Order savedOrder = orderRepository.save(order); + shoppingCart.getCartItems().clear(); + + return orderMapper.toDto(savedOrder); + } + + @Override + @Transactional + public OrderDto updateOrderStatus(Long orderId, UpdateOrderStatusRequestDto orderDto) { + Order order = orderRepository.findById(orderId) + .orElseThrow(() -> new EntityNotFoundException("Can't find order with id: " + + orderId)); + + order.setStatus(orderDto.getStatus()); + return orderMapper.toDto(order); + } + + @Override + public List getOrderItems(Long userId, Long orderId) { + Order order = getOrderForUser(userId, orderId); + return order.getOrderItems().stream() + .map(orderItemMapper::toDto) + .toList(); + } + + @Override + public OrderItemDto getOrderItem(Long userId, Long orderId, Long itemId) { + Order order = getOrderForUser(userId, orderId); + return order.getOrderItems().stream() + .filter(item -> item.getId().equals(itemId)) + .findFirst() + .map(orderItemMapper::toDto) + .orElseThrow(() -> new EntityNotFoundException("Can't find item with id: " + + itemId + " in order: " + orderId)); + } + + private Order getOrderForUser(Long userId, Long orderId) { + Order order = orderRepository.findById(orderId) + .orElseThrow(() -> new EntityNotFoundException("Can't find order with id: " + + orderId)); + if (!order.getUser().getId().equals(userId)) { + throw new RuntimeException("You don't have access to this order"); + } + return order; + } +} diff --git "a/src/main/resources/db/changelog/changes/07-\321\201reate-orders-table.yaml" "b/src/main/resources/db/changelog/changes/07-\321\201reate-orders-table.yaml" new file mode 100644 index 0000000..0a697e9 --- /dev/null +++ "b/src/main/resources/db/changelog/changes/07-\321\201reate-orders-table.yaml" @@ -0,0 +1,3 @@ +databaseChangeLog: + - changeSet: + From 79adbd7561ef9622c20ade600c6ffdafa60de664 Mon Sep 17 00:00:00 2001 From: IlonaChupa Date: Sun, 31 May 2026 17:32:38 +0300 Subject: [PATCH 2/4] fixed --- .../spring/springbootintro/controller/OrderController.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/main/java/spring/springbootintro/controller/OrderController.java b/src/main/java/spring/springbootintro/controller/OrderController.java index 3a2f99b..5ee5a81 100644 --- a/src/main/java/spring/springbootintro/controller/OrderController.java +++ b/src/main/java/spring/springbootintro/controller/OrderController.java @@ -36,7 +36,8 @@ public class OrderController { @ResponseStatus(HttpStatus.CREATED) @PreAuthorize("hasRole('USER')") @Operation(summary = "Create an order", - description = "Creates a new order from the user's current shopping cart and clears the cart.") + description = "Creates a new order from the user's current" + + " shopping cart and clears the cart.") public OrderDto createOrder(Authentication authentication, @RequestBody @Valid CreateOrderRequestDto requestDto) { User user = (User) authentication.getPrincipal(); From e47f7ad6632ca9f15f8fab10e529705c04aec467 Mon Sep 17 00:00:00 2001 From: IlonaChupa Date: Tue, 2 Jun 2026 00:54:27 +0300 Subject: [PATCH 3/4] fixed --- .../controller/OrderController.java | 3 +- .../exception/OrderProcessingException.java | 7 ++ .../spring/springbootintro/model/Order.java | 6 +- .../springbootintro/model/OrderItem.java | 4 +- .../repository/OrderRepository.java | 6 +- .../springbootintro/service/OrderService.java | 3 +- .../service/impl/OrderServiceImpl.java | 16 ++- .../07-\321\201reate-orders-table.yaml" | 105 ++++++++++++++++++ 8 files changed, 134 insertions(+), 16 deletions(-) create mode 100644 src/main/java/spring/springbootintro/exception/OrderProcessingException.java diff --git a/src/main/java/spring/springbootintro/controller/OrderController.java b/src/main/java/spring/springbootintro/controller/OrderController.java index 5ee5a81..87750f4 100644 --- a/src/main/java/spring/springbootintro/controller/OrderController.java +++ b/src/main/java/spring/springbootintro/controller/OrderController.java @@ -5,6 +5,7 @@ import jakarta.validation.Valid; import java.util.List; import lombok.RequiredArgsConstructor; +import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; import org.springframework.http.HttpStatus; import org.springframework.security.access.prepost.PreAuthorize; @@ -48,7 +49,7 @@ public OrderDto createOrder(Authentication authentication, @PreAuthorize("hasRole('USER')") @Operation(summary = "Get order history", description = "Retrieves the order history for the currently authenticated user.") - public List getOrderHistory(Authentication authentication, Pageable pageable) { + public Page getOrderHistory(Authentication authentication, Pageable pageable) { User user = (User) authentication.getPrincipal(); return orderService.getOrderHistory(user.getId(), pageable); } diff --git a/src/main/java/spring/springbootintro/exception/OrderProcessingException.java b/src/main/java/spring/springbootintro/exception/OrderProcessingException.java new file mode 100644 index 0000000..fb0b3a5 --- /dev/null +++ b/src/main/java/spring/springbootintro/exception/OrderProcessingException.java @@ -0,0 +1,7 @@ +package spring.springbootintro.exception; + +public class OrderProcessingException extends RuntimeException { + public OrderProcessingException(String message) { + super(message); + } +} diff --git a/src/main/java/spring/springbootintro/model/Order.java b/src/main/java/spring/springbootintro/model/Order.java index 4a49409..8b66907 100644 --- a/src/main/java/spring/springbootintro/model/Order.java +++ b/src/main/java/spring/springbootintro/model/Order.java @@ -1,7 +1,9 @@ package spring.springbootintro.model; +import jakarta.persistence.CascadeType; import jakarta.persistence.Column; import jakarta.persistence.Entity; +import jakarta.persistence.FetchType; import jakarta.persistence.GeneratedValue; import jakarta.persistence.GenerationType; import jakarta.persistence.Id; @@ -29,7 +31,7 @@ public class Order { @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; - @ManyToOne + @ManyToOne(fetch = FetchType.LAZY, optional = false) @JoinColumn(name = "user_id", nullable = false) private User user; @@ -45,7 +47,7 @@ public class Order { @Column(nullable = false) private String shippingAddress; - @OneToMany + @OneToMany(mappedBy = "order", cascade = CascadeType.ALL, orphanRemoval = true) private Set orderItems; private boolean isDeleted = false; diff --git a/src/main/java/spring/springbootintro/model/OrderItem.java b/src/main/java/spring/springbootintro/model/OrderItem.java index 51a0b8d..3a60746 100644 --- a/src/main/java/spring/springbootintro/model/OrderItem.java +++ b/src/main/java/spring/springbootintro/model/OrderItem.java @@ -2,6 +2,7 @@ import jakarta.persistence.Column; import jakarta.persistence.Entity; +import jakarta.persistence.FetchType; import jakarta.persistence.GeneratedValue; import jakarta.persistence.GenerationType; import jakarta.persistence.Id; @@ -26,7 +27,7 @@ public class OrderItem { @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; - @ManyToOne + @ManyToOne(fetch = FetchType.LAZY, optional = false) @JoinColumn(name = "order_id") private Order order; @@ -34,6 +35,7 @@ public class OrderItem { @JoinColumn(name = "book_id") private Book book; + @Column private int quantity; @Column(nullable = false) diff --git a/src/main/java/spring/springbootintro/repository/OrderRepository.java b/src/main/java/spring/springbootintro/repository/OrderRepository.java index a9f3fe4..1788d96 100644 --- a/src/main/java/spring/springbootintro/repository/OrderRepository.java +++ b/src/main/java/spring/springbootintro/repository/OrderRepository.java @@ -1,6 +1,7 @@ package spring.springbootintro.repository; -import java.util.List; +import java.util.Optional; +import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.stereotype.Repository; @@ -9,6 +10,7 @@ @Repository public interface OrderRepository extends JpaRepository { - List findAllByUserId(Long userId, Pageable pageable); + Page findAllByUserId(Long userId, Pageable pageable); + Optional findByIdAndUserId(Long orderId, Long userId); } diff --git a/src/main/java/spring/springbootintro/service/OrderService.java b/src/main/java/spring/springbootintro/service/OrderService.java index e35ca1a..9fde7aa 100644 --- a/src/main/java/spring/springbootintro/service/OrderService.java +++ b/src/main/java/spring/springbootintro/service/OrderService.java @@ -1,6 +1,7 @@ package spring.springbootintro.service; import java.util.List; +import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; import spring.springbootintro.dto.CreateOrderRequestDto; import spring.springbootintro.dto.OrderDto; @@ -9,7 +10,7 @@ public interface OrderService { - List getOrderHistory(Long userId, Pageable pageable); + Page getOrderHistory(Long userId, Pageable pageable); OrderDto createOrder(Long userId, CreateOrderRequestDto requestDto); diff --git a/src/main/java/spring/springbootintro/service/impl/OrderServiceImpl.java b/src/main/java/spring/springbootintro/service/impl/OrderServiceImpl.java index b6614a0..13283bc 100644 --- a/src/main/java/spring/springbootintro/service/impl/OrderServiceImpl.java +++ b/src/main/java/spring/springbootintro/service/impl/OrderServiceImpl.java @@ -6,6 +6,7 @@ import java.util.List; import java.util.Set; import lombok.RequiredArgsConstructor; +import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; @@ -14,6 +15,7 @@ import spring.springbootintro.dto.OrderItemDto; import spring.springbootintro.dto.UpdateOrderStatusRequestDto; import spring.springbootintro.exception.EntityNotFoundException; +import spring.springbootintro.exception.OrderProcessingException; import spring.springbootintro.mapper.OrderItemMapper; import spring.springbootintro.mapper.OrderMapper; import spring.springbootintro.model.CartItem; @@ -35,10 +37,9 @@ public class OrderServiceImpl implements OrderService { private final OrderItemMapper orderItemMapper; @Override - public List getOrderHistory(Long userId, Pageable pageable) { - return orderRepository.findAllByUserId(userId, pageable).stream() - .map(orderMapper::toDto) - .toList(); + public Page getOrderHistory(Long userId, Pageable pageable) { + return orderRepository.findAllByUserId(userId, pageable) + .map(orderMapper::toDto); } @Override @@ -49,7 +50,7 @@ public OrderDto createOrder(Long userId, CreateOrderRequestDto requestDto) { + " shopping cart for user with id: " + userId)); if (shoppingCart.getCartItems().isEmpty()) { - throw new IllegalArgumentException("Can't place order with empty shopping cart"); + throw new OrderProcessingException("Can't place order with empty shopping cart"); } Order order = new Order(); @@ -115,12 +116,9 @@ public OrderItemDto getOrderItem(Long userId, Long orderId, Long itemId) { } private Order getOrderForUser(Long userId, Long orderId) { - Order order = orderRepository.findById(orderId) + Order order = orderRepository.findByIdAndUserId(orderId, userId) .orElseThrow(() -> new EntityNotFoundException("Can't find order with id: " + orderId)); - if (!order.getUser().getId().equals(userId)) { - throw new RuntimeException("You don't have access to this order"); - } return order; } } diff --git "a/src/main/resources/db/changelog/changes/07-\321\201reate-orders-table.yaml" "b/src/main/resources/db/changelog/changes/07-\321\201reate-orders-table.yaml" index 0a697e9..14510b2 100644 --- "a/src/main/resources/db/changelog/changes/07-\321\201reate-orders-table.yaml" +++ "b/src/main/resources/db/changelog/changes/07-\321\201reate-orders-table.yaml" @@ -1,3 +1,108 @@ databaseChangeLog: - changeSet: + id: create-orders-table + author: ilonachupa + changes: + - createTable: + tableName: orders + columns: + - column: + name: id + type: BIGINT + autoIncrement: true + constraints: + primaryKey: true + nullable: false + - column: + name: user_id + type: BIGINT + constraints: + nullable: false + - column: + name: status + type: VARCHAR(255) + constraints: + nullable: false + - column: + name: total + type: DECIMAL(10, 2) + constraints: + nullable: false + - column: + name: order_date + type: DATETIME + constraints: + nullable: false + - column: + name: shipping_address + type: VARCHAR(255) + constraints: + nullable: false + - column: + name: is_deleted + type: BOOLEAN + defaultValueBoolean: false + constraints: + nullable: false + - addForeignKeyConstraint: + baseTableName: orders + baseColumnNames: user_id + referencedTableName: users + referencedColumnNames: id + constraintName: fk_orders_users + + - changeSet: + id: create-order-items-table + author: ilonachupa + changes: + - createTable: + tableName: order_items + columns: + - column: + name: id + type: BIGINT + autoIncrement: true + constraints: + primaryKey: true + nullable: false + - column: + name: order_id + type: BIGINT + constraints: + nullable: false + - column: + name: book_id + type: BIGINT + constraints: + nullable: false + - column: + name: quantity + type: INT + constraints: + nullable: false + - column: + name: price + type: DECIMAL(10, 2) + constraints: + nullable: false + - column: + name: is_deleted + type: BOOLEAN + defaultValueBoolean: false + constraints: + nullable: false + + - addForeignKeyConstraint: + baseTableName: order_items + baseColumnNames: order_id + referencedTableName: orders + referencedColumnNames: id + constraintName: fk_order_items_orders + + - addForeignKeyConstraint: + baseTableName: order_items + baseColumnNames: book_id + referencedTableName: books + referencedColumnNames: id + constraintName: fk_order_items_books From c99dfcee26eed2c6565f80c9bae0ab88a9e73f08 Mon Sep 17 00:00:00 2001 From: IlonaChupa Date: Tue, 2 Jun 2026 15:24:53 +0300 Subject: [PATCH 4/4] fixed --- .../java/spring/springbootintro/model/OrderItem.java | 10 ++++++++-- .../resources/db/changelog/db.changelog-master.yaml | 3 +++ 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/src/main/java/spring/springbootintro/model/OrderItem.java b/src/main/java/spring/springbootintro/model/OrderItem.java index 3a60746..789e760 100644 --- a/src/main/java/spring/springbootintro/model/OrderItem.java +++ b/src/main/java/spring/springbootintro/model/OrderItem.java @@ -10,8 +10,10 @@ import jakarta.persistence.ManyToOne; import jakarta.persistence.Table; import java.math.BigDecimal; +import lombok.EqualsAndHashCode; import lombok.Getter; import lombok.Setter; +import lombok.ToString; import org.hibernate.annotations.SQLDelete; import org.hibernate.annotations.SQLRestriction; @@ -29,13 +31,17 @@ public class OrderItem { @ManyToOne(fetch = FetchType.LAZY, optional = false) @JoinColumn(name = "order_id") + @ToString.Exclude + @EqualsAndHashCode.Exclude private Order order; - @ManyToOne + @ManyToOne(fetch = FetchType.LAZY) @JoinColumn(name = "book_id") + @ToString.Exclude + @EqualsAndHashCode.Exclude private Book book; - @Column + @Column(nullable = false) private int quantity; @Column(nullable = false) diff --git a/src/main/resources/db/changelog/db.changelog-master.yaml b/src/main/resources/db/changelog/db.changelog-master.yaml index f022e06..44f12cc 100644 --- a/src/main/resources/db/changelog/db.changelog-master.yaml +++ b/src/main/resources/db/changelog/db.changelog-master.yaml @@ -11,4 +11,7 @@ databaseChangeLog: file: db/changelog/changes/05-create-categories-table.yaml - include: file: db/changelog/changes/06-create-shopping-cart-tables.yaml + - include: + file: db/changelog/changes/07-сreate-orders-table.yaml +