Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

implemented order model #12

Merged
merged 5 commits into from
Jul 27, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
122 changes: 122 additions & 0 deletions src/main/java/mate/academy/controller/OrderController.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
package mate.academy.controller;

import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.responses.ApiResponse;
import io.swagger.v3.oas.annotations.responses.ApiResponses;
import io.swagger.v3.oas.annotations.tags.Tag;
import java.util.List;
import lombok.RequiredArgsConstructor;
import mate.academy.dto.order.OrderDto;
import mate.academy.dto.order.OrderItemDto;
import mate.academy.dto.order.PlaceOrderRequestDto;
import mate.academy.dto.order.UpdateOrderStatusRequestDto;
import mate.academy.model.User;
import mate.academy.service.OrderService;
import org.springdoc.core.annotations.ParameterObject;
import org.springframework.data.domain.Pageable;
import org.springframework.data.web.PageableDefault;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.security.core.annotation.AuthenticationPrincipal;
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.RestController;

@RestController
@RequestMapping("/orders")
@RequiredArgsConstructor
@Tag(name = "Order Controller", description = "Operations pertaining to orders in Online Book App")
public class OrderController {
private final OrderService orderService;

@PreAuthorize("hasRole('USER')")
@PostMapping
@Operation(summary = "Place an order",
description = "Allows a user to place an order with the items in their shopping cart.")
@ApiResponses({
@ApiResponse(responseCode = "200", description = "Order placed successfully"),
@ApiResponse(responseCode = "400", description = "Invalid input data"),
@ApiResponse(responseCode = "401", description = "Unauthorized"),
@ApiResponse(responseCode = "404", description = "User not found")
})
public OrderDto placeOrder(
@AuthenticationPrincipal User userDetails,
@RequestBody @Parameter(description = "Details for placing an order", required = true)
PlaceOrderRequestDto requestDto) {
return orderService.createOrder(userDetails.getId(), requestDto.shippingAddress());
}

@PreAuthorize("hasRole('USER')")
@GetMapping
@Operation(summary = "Get order history",
description = "Allows a user to view their order history.")
@ApiResponses({
@ApiResponse(responseCode = "200",
description = "Order history retrieved successfully"),
@ApiResponse(responseCode = "401", description = "Unauthorized"),
@ApiResponse(responseCode = "404", description = "User not found")
})
public List<OrderDto> getOrderHistory(
@AuthenticationPrincipal User userDetails,
@ParameterObject @PageableDefault Pageable pageable) {
return orderService.getOrderHistory(userDetails.getId(), pageable);
}

@PreAuthorize("hasRole('USER')")
@GetMapping("/{orderId}/items")
@Operation(summary = "Get order items",
description = "Allows a user to view all items in a specific order.")
@ApiResponses({
@ApiResponse(responseCode = "200", description = "Order items retrieved successfully"),
@ApiResponse(responseCode = "401", description = "Unauthorized"),
@ApiResponse(responseCode = "404", description = "Order not found")
})
public List<OrderItemDto> getOrderItems(
@PathVariable
@Parameter(description = "ID of the order to retrieve items from", required = true)
Long orderId,
@ParameterObject @PageableDefault Pageable pageable) {
return orderService.getOrderItems(orderId, pageable);
}

@PreAuthorize("hasRole('USER')")
@GetMapping("/{orderId}/items/{itemId}")
@Operation(summary = "Get specific order item",
description = "Allows a user to view a specific item in an order.")
@ApiResponses({
@ApiResponse(responseCode = "200", description = "Order item retrieved successfully"),
@ApiResponse(responseCode = "401", description = "Unauthorized"),
@ApiResponse(responseCode = "404", description = "Order or item not found")
})
public OrderItemDto getOrderItem(
@PathVariable
@Parameter(description = "ID of the order to retrieve the item from", required = true)
Long orderId,
@PathVariable @Parameter(description = "ID of the item to retrieve", required = true)
Long itemId) {
return orderService.getOrderItem(orderId, itemId);
}

@PreAuthorize("hasRole('ADMIN')")
@PatchMapping("/{orderId}")
@Operation(summary = "Update order status",
description = "Allows an admin to update the status of an order.")
@ApiResponses({
@ApiResponse(responseCode = "200", description = "Order status updated successfully"),
@ApiResponse(responseCode = "400", description = "Invalid status"),
@ApiResponse(responseCode = "401", description = "Unauthorized"),
@ApiResponse(responseCode = "404", description = "Order not found")
})
public OrderDto updateOrderStatus(
@PathVariable
@Parameter(description = "ID of the order to update", required = true) Long orderId,
@RequestBody
@Parameter(description = "Details for updating the order status", required = true)
UpdateOrderStatusRequestDto requestDto) {
return orderService.updateOrderStatus(orderId, requestDto.status().name());
}
}
16 changes: 16 additions & 0 deletions src/main/java/mate/academy/dto/order/OrderDto.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package mate.academy.dto.order;

import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.Set;

public record OrderDto(
Long id,
Long userId,
String status,
BigDecimal total,
LocalDateTime orderDate,
String shippingAddress,
Set<OrderItemDto> orderItems
) {
}
11 changes: 11 additions & 0 deletions src/main/java/mate/academy/dto/order/OrderItemDto.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package mate.academy.dto.order;

import java.math.BigDecimal;

public record OrderItemDto(
Long id,
Long orderId,
Long bookId,
int quantity,
BigDecimal price) {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package mate.academy.dto.order;

import jakarta.validation.constraints.NotBlank;

public record PlaceOrderRequestDto(
@NotBlank String shippingAddress
) {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package mate.academy.dto.order;

import jakarta.validation.constraints.NotNull;
import mate.academy.model.Order;

public record UpdateOrderStatusRequestDto(
@NotNull Order.Status status
) {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package mate.academy.exception;

public class OrderProcessingException extends RuntimeException {
public OrderProcessingException(String message) {
super(message);
}
}
18 changes: 18 additions & 0 deletions src/main/java/mate/academy/mapper/OrderItemMapper.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package mate.academy.mapper;

import mate.academy.config.MapperConfig;
import mate.academy.dto.order.OrderItemDto;
import mate.academy.model.OrderItem;
import org.mapstruct.Mapper;
import org.mapstruct.Mapping;

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

@Mapping(source = "orderId", target = "order.id")
@Mapping(source = "bookId", target = "book.id")
OrderItem toModel(OrderItemDto orderItemDto);
}
16 changes: 16 additions & 0 deletions src/main/java/mate/academy/mapper/OrderMapper.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package mate.academy.mapper;

import mate.academy.config.MapperConfig;
import mate.academy.dto.order.OrderDto;
import mate.academy.model.Order;
import org.mapstruct.Mapper;
import org.mapstruct.Mapping;

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

@Mapping(source = "userId", target = "user.id")
Order toModel(OrderDto orderDto);
}
61 changes: 61 additions & 0 deletions src/main/java/mate/academy/model/Order.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
package mate.academy.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.HashSet;
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.SQLRestriction;

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

public enum Status {
COMPLETED,
PENDING,
DELIVERED
}
}
42 changes: 42 additions & 0 deletions src/main/java/mate/academy/model/OrderItem.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
package mate.academy.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.Getter;
import lombok.Setter;
import org.hibernate.annotations.SQLDelete;
import org.hibernate.annotations.SQLRestriction;

@Entity
@Getter
@Setter
@SQLDelete(sql = "UPDATE order_items set is_deleted = true WHERE id=?")
@SQLRestriction("is_deleted=false")
@Table(name = "order_items")
public class OrderItem {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;

@ManyToOne(fetch = FetchType.LAZY, optional = false)
@JoinColumn(name = "order_id", nullable = false)
private Order order;

@ManyToOne(fetch = FetchType.LAZY, optional = false)
@JoinColumn(name = "book_id", nullable = false)
private Book book;
@Column(nullable = false)
private int quantity;
@Column(nullable = false)
private BigDecimal price;
@Column(nullable = false, name = "is_deleted", columnDefinition = "BIT")
private boolean isDeleted = false;
}
10 changes: 10 additions & 0 deletions src/main/java/mate/academy/repository/order/OrderRepository.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package mate.academy.repository.order;

import java.util.List;
import mate.academy.model.Order;
import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.repository.JpaRepository;

public interface OrderRepository extends JpaRepository<Order, Long> {
List<Order> findByUserId(Long userId, Pageable pageable);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package mate.academy.repository.orderitem;

import java.util.List;
import java.util.Optional;
import mate.academy.model.OrderItem;
import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.repository.JpaRepository;

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

Optional<OrderItem> findByOrderIdAndId(Long orderId, Long id);
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package mate.academy.repository.shoppingcart;

import java.util.Optional;
import mate.academy.model.ShoppingCart;
import org.springframework.data.jpa.repository.EntityGraph;
import org.springframework.data.jpa.repository.JpaRepository;
Expand All @@ -8,5 +9,5 @@
@Repository
public interface ShoppingCartRepository extends JpaRepository<ShoppingCart, Long> {
@EntityGraph(attributePaths = {"cartItems", "cartItems.book"})
ShoppingCart findByUserId(Long userId);
Optional<ShoppingCart> findByUserId(Long userId);
}
18 changes: 18 additions & 0 deletions src/main/java/mate/academy/service/OrderService.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package mate.academy.service;

import java.util.List;
import mate.academy.dto.order.OrderDto;
import mate.academy.dto.order.OrderItemDto;
import org.springframework.data.domain.Pageable;

public interface OrderService {
OrderDto createOrder(Long userId, String shippingAddress);

List<OrderDto> getOrderHistory(Long userId, Pageable pageable);

List<OrderItemDto> getOrderItems(Long orderId, Pageable pageable);

OrderItemDto getOrderItem(Long orderId, Long itemId);

OrderDto updateOrderStatus(Long orderId, String status);
}
Loading
Loading