Skip to content

Commit

Permalink
implemented order model
Browse files Browse the repository at this point in the history
  • Loading branch information
fmIst0 committed Sep 17, 2023
1 parent ae430e3 commit 4ee8a78
Show file tree
Hide file tree
Showing 21 changed files with 565 additions and 3 deletions.
82 changes: 82 additions & 0 deletions src/main/java/com/bookstore/controller/OrderController.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
package com.bookstore.controller;

import com.bookstore.dto.order.OrderCreateDto;
import com.bookstore.dto.order.OrderResponseDto;
import com.bookstore.dto.order.OrderUpdateDto;
import com.bookstore.dto.orderitem.OrderItemResponseDto;
import com.bookstore.model.User;
import com.bookstore.service.OrderService;
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;

@Tag(name = "Order management", description = "Endpoints for managing orders")
@RequiredArgsConstructor
@RestController
@RequestMapping(value = "/api/orders")
public class OrderController {
private final OrderService orderService;

@PostMapping
@PreAuthorize("hasRole('USER')")
@ResponseStatus(HttpStatus.CREATED)
@Operation(summary = "Place an order",
description = "Place an order. Create a new order from shopping cart")
public OrderResponseDto createOrder(Authentication authentication,
@RequestBody @Valid OrderCreateDto orderCreateDto) {
User user = (User) authentication.getPrincipal();
return orderService.saveOrderFromCart(user.getId(), orderCreateDto);
}

@GetMapping
@PreAuthorize("hasRole('USER')")
@Operation(summary = "Retrieve user's order history",
description = "User can see all his orders history")
public List<OrderResponseDto> getAllUsersOrders(Authentication authentication,
Pageable pageable) {
User user = (User) authentication.getPrincipal();
return orderService.getAllUserOrders(user.getId(), pageable);
}

@GetMapping(value = "/{orderId}/items")
@PreAuthorize("hasRole('USER')")
@Operation(summary = "Retrieve all OrderItems for a specific order",
description = "Retrieve all OrderItems for a specific order")
public List<OrderItemResponseDto> getAllOrderItemsByOrderId(@PathVariable Long orderId,
Pageable pageable) {
return orderService.getAllOrderItemsByOrderId(orderId, pageable);
}

@GetMapping(value = "/{orderId}/items/{itemId}")
@PreAuthorize("hasRole('USER')")
@Operation(summary = "Retrieve a specific OrderItem within an order",
description = "Retrieve a specific OrderItem within an order")
public OrderItemResponseDto getOrderItemWithinAnOrder(@PathVariable Long orderId,
@PathVariable Long itemId) {
return orderService.getOrderItemWithinAnOrder(orderId, itemId);
}

@PatchMapping(value = "/{id}")
@PreAuthorize("hasRole('ADMIN')")
@ResponseStatus(HttpStatus.ACCEPTED)
@Operation(summary = "Update order status(for admins)",
description = "Update order status")
public void updateOrderStatus(@PathVariable Long id,
@RequestBody @Valid OrderUpdateDto orderUpdateDto) {
orderService.updateOrderStatus(id, orderUpdateDto);
}
}
10 changes: 10 additions & 0 deletions src/main/java/com/bookstore/dto/order/OrderCreateDto.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package com.bookstore.dto.order;

import jakarta.validation.constraints.NotBlank;
import lombok.Data;

@Data
public class OrderCreateDto {
@NotBlank
private String shippingAddress;
}
18 changes: 18 additions & 0 deletions src/main/java/com/bookstore/dto/order/OrderResponseDto.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package com.bookstore.dto.order;

import com.bookstore.dto.orderitem.OrderItemResponseDto;
import com.bookstore.model.Order;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.Set;
import lombok.Data;

@Data
public class OrderResponseDto {
private Long id;
private Long userId;
private Set<OrderItemResponseDto> orderItems;
private LocalDateTime orderDate;
private BigDecimal total;
private Order.Status status;
}
11 changes: 11 additions & 0 deletions src/main/java/com/bookstore/dto/order/OrderUpdateDto.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package com.bookstore.dto.order;

import com.bookstore.model.Order;
import jakarta.validation.constraints.NotNull;
import lombok.Data;

@Data
public class OrderUpdateDto {
@NotNull
private Order.Status status;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package com.bookstore.dto.orderitem;

import lombok.Data;

@Data
public class OrderItemResponseDto {
private Long id;
private Long bookId;
private int quantity;
}
19 changes: 19 additions & 0 deletions src/main/java/com/bookstore/mapper/OrderItemMapper.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package com.bookstore.mapper;

import com.bookstore.config.MapperConfig;
import com.bookstore.dto.orderitem.OrderItemResponseDto;
import com.bookstore.model.CartItem;
import com.bookstore.model.OrderItem;
import org.mapstruct.Mapper;
import org.mapstruct.Mapping;

@Mapper(config = MapperConfig.class)
public interface OrderItemMapper {
@Mapping(target = "bookId", source = "orderItem.book.id")
OrderItemResponseDto toDto(OrderItem orderItem);

@Mapping(target = "id", ignore = true)
@Mapping(target = "order", ignore = true)
@Mapping(target = "price", source = "cartItem.book.price")
OrderItem cartItemToOrderItem(CartItem cartItem);
}
41 changes: 41 additions & 0 deletions src/main/java/com/bookstore/mapper/OrderMapper.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
package com.bookstore.mapper;

import com.bookstore.config.MapperConfig;
import com.bookstore.dto.order.OrderResponseDto;
import com.bookstore.model.Order;
import com.bookstore.model.ShoppingCart;
import java.math.BigDecimal;
import org.mapstruct.AfterMapping;
import org.mapstruct.Mapper;
import org.mapstruct.Mapping;
import org.mapstruct.MappingTarget;

@Mapper(config = MapperConfig.class, uses = OrderItemMapper.class)
public interface OrderMapper {
@Mapping(target = "orderItems", source = "orderItems")
@Mapping(target = "userId", source = "order.user.id")
OrderResponseDto toDto(Order order);

@Mapping(target = "id", ignore = true)
@Mapping(target = "total", ignore = true)
@Mapping(target = "status", expression = "java(com.bookstore.model.Order.Status.PENDING)")
@Mapping(target = "orderDate", expression = "java(java.time.LocalDateTime.now())")
Order shoppingCartToOrder(ShoppingCart shoppingCart);

@AfterMapping
default void setOrderTotal(@MappingTarget Order order, ShoppingCart shoppingCart) {
order.setTotal(getTotal(shoppingCart));
}

private BigDecimal getTotal(ShoppingCart shoppingCart) {
return shoppingCart.getCartItems()
.stream()
.map(cartItem -> cartItem.getBook()
.getPrice()
.multiply(
BigDecimal.valueOf(cartItem.getQuantity())
)
)
.reduce(BigDecimal.ZERO, BigDecimal::add);
}
}
62 changes: 62 additions & 0 deletions src/main/java/com/bookstore/model/Order.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
package com.bookstore.model;

import jakarta.persistence.CascadeType;
import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.EnumType;
import jakarta.persistence.Enumerated;
import jakarta.persistence.FetchType;
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.EqualsAndHashCode;
import lombok.Getter;
import lombok.Setter;
import lombok.ToString;
import org.hibernate.annotations.SQLDelete;
import org.hibernate.annotations.Where;

@Getter
@Setter
@SQLDelete(sql = "UPDATE orders SET is_deleted=true WHERE id=?")
@Where(clause = "is_deleted=false")
@Entity
@EqualsAndHashCode
@Table(name = "orders")
public class Order {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "user_id", nullable = false)
private User user;
@Enumerated(EnumType.STRING)
@Column(name = "status", nullable = false)
private Status status;
@Column(nullable = false)
private BigDecimal total;
@Column(name = "order_date", nullable = false)
private LocalDateTime orderDate;
@Column(name = "shipping_address", nullable = false)
private String shippingAddress;
@ToString.Exclude
@EqualsAndHashCode.Exclude
@OneToMany(mappedBy = "order", cascade = CascadeType.ALL, orphanRemoval = true)
private Set<OrderItem> orderItems;
@Column(name = "is_deleted", nullable = false)
private boolean isDeleted = false;

public enum Status {
PENDING,
SHIPPABLE,
DELIVERED,
COMPLETED
}
}
36 changes: 36 additions & 0 deletions src/main/java/com/bookstore/model/OrderItem.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
package com.bookstore.model;

import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.FetchType;
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.EqualsAndHashCode;
import lombok.Getter;
import lombok.Setter;

@Getter
@Setter
@Entity
@EqualsAndHashCode
@Table(name = "order_items")
public class OrderItem {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "order_id", nullable = false)
private Order order;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "book_id", nullable = false)
private Book book;
@Column(nullable = false)
private int quantity;
@Column(nullable = false)
private BigDecimal price;
}
2 changes: 1 addition & 1 deletion src/main/java/com/bookstore/model/ShoppingCart.java
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ public class ShoppingCart {
private User user;
@ToString.Exclude
@EqualsAndHashCode.Exclude
@OneToMany(mappedBy = "shoppingCart", cascade = CascadeType.ALL, orphanRemoval = true)
@OneToMany(mappedBy = "shoppingCart", cascade = CascadeType.ALL)
private Set<CartItem> cartItems;

public void addCartItemToSet(CartItem cartItem) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,5 @@
import org.springframework.data.jpa.repository.JpaRepository;

public interface CartItemRepository extends JpaRepository<CartItem, Long> {
void deleteCartItemsByShoppingCartId(Long shoppingCartId);
}
12 changes: 12 additions & 0 deletions src/main/java/com/bookstore/repository/order/OrderRepository.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package com.bookstore.repository.order;

import com.bookstore.model.Order;
import java.util.List;
import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.repository.EntityGraph;
import org.springframework.data.jpa.repository.JpaRepository;

public interface OrderRepository extends JpaRepository<Order, Long> {
@EntityGraph(attributePaths = "orderItems")
List<Order> getAllByUserId(Long userId, Pageable pageable);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package com.bookstore.repository.orderitem;

import com.bookstore.model.OrderItem;
import java.util.List;
import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;

public interface OrderItemRepository extends JpaRepository<OrderItem, Long> {
List<OrderItem> findOrderItemsByOrderId(Long orderId, Pageable pageable);

@Query(value = "FROM OrderItem oi "
+ "WHERE oi.order.id = :orderId "
+ "AND oi.id = :itemId")
OrderItem findOrderItemByOrderIdAndByItemId(Long orderId, Long itemId);
}
20 changes: 20 additions & 0 deletions src/main/java/com/bookstore/service/OrderService.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package com.bookstore.service;

import com.bookstore.dto.order.OrderCreateDto;
import com.bookstore.dto.order.OrderResponseDto;
import com.bookstore.dto.order.OrderUpdateDto;
import com.bookstore.dto.orderitem.OrderItemResponseDto;
import java.util.List;
import org.springframework.data.domain.Pageable;

public interface OrderService {
OrderResponseDto saveOrderFromCart(Long userId, OrderCreateDto orderCreateDto);

void updateOrderStatus(Long id, OrderUpdateDto orderUpdateDto);

OrderItemResponseDto getOrderItemWithinAnOrder(Long orderId, Long itemId);

List<OrderResponseDto> getAllUserOrders(Long userId, Pageable pageable);

List<OrderItemResponseDto> getAllOrderItemsByOrderId(Long orderId, Pageable pageable);
}
5 changes: 5 additions & 0 deletions src/main/java/com/bookstore/service/ShoppingCartService.java
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import com.bookstore.dto.cartitem.CartItemCreateDto;
import com.bookstore.dto.cartitem.CartItemUpdateDto;
import com.bookstore.dto.shoppingcart.ShoppingCartDto;
import com.bookstore.model.ShoppingCart;
import java.util.List;
import org.springframework.data.domain.Pageable;

Expand All @@ -16,4 +17,8 @@ public interface ShoppingCartService {
ShoppingCartDto update(Long id, Long cartItemId, CartItemUpdateDto cartItemUpdateDto);

void deleteCartItemFromTheCart(Long id);

ShoppingCart getShoppingCartByUserId(Long id);

void cleanShoppingCart(ShoppingCart shoppingCart);
}
Loading

0 comments on commit 4ee8a78

Please sign in to comment.