Skip to content
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
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
package mate.academy.bookshop.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 mate.academy.bookshop.dto.order.OrderRequestDto;
import mate.academy.bookshop.dto.order.OrderResponseDto;
import mate.academy.bookshop.dto.order.OrderUpdateStatusDto;
import mate.academy.bookshop.dto.orderitem.OrderItemResponseDto;
import mate.academy.bookshop.model.User;
import mate.academy.bookshop.service.order.OrderService;
import org.springframework.data.domain.Pageable;
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.RestController;

@RestController
@RequiredArgsConstructor
@Tag(name = "Orders", description = "Operations for managing orders")
@RequestMapping("/orders")
public class OrderController {
private final OrderService orderService;

@Operation(summary = "Get all orders for the authenticated user",
description = "Retrieves a list of orders for the currently authenticated user.")
@GetMapping
public List<OrderResponseDto> getOrders(Pageable pageable, Authentication authentication) {
Long authenticatedUserId = getAuthenticatedUserId(authentication);
return orderService.getOrderByUserId(pageable, authenticatedUserId);
}

@Operation(summary = "Create a new order",
description = "Creates a new order for the authenticated user.")
@PostMapping
public OrderResponseDto createOrder(Authentication authentication,
@RequestBody @Valid OrderRequestDto orderRequestDto) {
Long authenticatedUserId = getAuthenticatedUserId(authentication);
return orderService.createOrderByUserId(authenticatedUserId,
orderRequestDto);
}

@PreAuthorize("hasRole('ROLE_ADMIN')")
@Operation(summary = "Update the status of an order",
description = "Updates the status of an order identified"
+ " by orderId for the authenticated user.")
@PatchMapping("/{orderId}")
public OrderResponseDto updateStatus(
@PathVariable Long orderId,
@RequestBody @Valid OrderUpdateStatusDto orderUpdateStatusDto) {
return orderService.updateStatusByOrderId(orderId, orderUpdateStatusDto);
}

@Operation(summary = "Get all items in a specific order",
description = "Retrieves the list of items for a specific"
+ " order identified by orderId for the authenticated user.")
@GetMapping("/{orderId}/items")
public List<OrderItemResponseDto> getOrderItems(@PathVariable Long orderId,
Authentication authentication) {
Long authenticatedUserId = getAuthenticatedUserId(authentication);
return orderService.getOrderItemsByOrderId(orderId, authenticatedUserId);
}

@Operation(summary = "Get a specific item from an order",
description = "Retrieves a specific item from an order "
+ "by itemId, within the order identified by orderId.")
@GetMapping("/{orderId}/items/{itemId}")
public OrderItemResponseDto getOrderItem(@PathVariable Long orderId,

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

provide Authentication

@PathVariable Long itemId,
Authentication authentication) {
Long authenticatedUserId = getAuthenticatedUserId(authentication);
return orderService.getOrderItemFromOrderById(orderId, itemId, authenticatedUserId);
}

private Long getAuthenticatedUserId(Authentication authentication) {
return ((User) authentication.getPrincipal()).getId();
}
}
10 changes: 10 additions & 0 deletions src/main/java/mate/academy/bookshop/dto/order/OrderRequestDto.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package mate.academy.bookshop.dto.order;

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

@Data
public class OrderRequestDto {
@NotBlank
private String shippingAddress;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package mate.academy.bookshop.dto.order;

import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.List;
import lombok.Data;
import mate.academy.bookshop.dto.orderitem.OrderItemResponseDto;

@Data
public class OrderResponseDto {
private Long id;

private Long userId;

private List<OrderItemResponseDto> orderItemsDto;

private LocalDateTime orderDate;

private BigDecimal total;

private String status;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package mate.academy.bookshop.dto.order;

import jakarta.validation.constraints.NotBlank;

public record OrderUpdateStatusDto(@NotBlank String status) {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package mate.academy.bookshop.dto.orderitem;

import java.math.BigDecimal;
import lombok.Data;

@Data
public class OrderItemResponseDto {
private Long id;

private Long bookId;

private int quantity;

private BigDecimal price;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package mate.academy.bookshop.exceptions;

public class OrderProcessingException extends RuntimeException {
public OrderProcessingException(String message) {
super(message);
}
}

20 changes: 20 additions & 0 deletions src/main/java/mate/academy/bookshop/mapper/OrderItemMapper.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package mate.academy.bookshop.mapper;

import java.util.List;
import mate.academy.bookshop.config.MapperConfig;
import mate.academy.bookshop.dto.orderitem.OrderItemResponseDto;
import mate.academy.bookshop.model.OrderItem;
import org.mapstruct.IterableMapping;
import org.mapstruct.Mapper;
import org.mapstruct.Mapping;
import org.mapstruct.Named;

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

@IterableMapping(qualifiedByName = "orderItemToDto")
List<OrderItemResponseDto> toOrderItemDtoList(List<OrderItem> orderItems);
}
27 changes: 27 additions & 0 deletions src/main/java/mate/academy/bookshop/mapper/OrderMapper.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package mate.academy.bookshop.mapper;

import java.util.List;
import mate.academy.bookshop.config.MapperConfig;
import mate.academy.bookshop.dto.order.OrderRequestDto;
import mate.academy.bookshop.dto.order.OrderResponseDto;
import mate.academy.bookshop.dto.order.OrderUpdateStatusDto;
import mate.academy.bookshop.model.Order;
import org.mapstruct.Mapper;
import org.mapstruct.Mapping;
import org.mapstruct.MappingTarget;

@Mapper(config = MapperConfig.class, uses = OrderItemMapper.class)
public interface OrderMapper {
@Mapping(target = "userId", source = "user.id")
@Mapping(target = "orderItemsDto", source = "orderItems", qualifiedByName = "orderItemToDto")
@Mapping(target = "status", expression = "java(order.getStatus().name())")
@Mapping(target = "orderDate", source = "orderDate", dateFormat = "yyyy-MM-dd HH:mm:ss")
OrderResponseDto toDto(Order order);

List<OrderResponseDto> toDto(List<Order> order);

Order toEntity(OrderRequestDto orderRequestDto);

void updateOrderStatusFromDto(OrderUpdateStatusDto orderUpdateStatusDto,
@MappingTarget Order order);
}
62 changes: 62 additions & 0 deletions src/main/java/mate/academy/bookshop/model/Order.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
package mate.academy.bookshop.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.Getter;
import lombok.Setter;
import org.hibernate.annotations.SQLDelete;
import org.hibernate.annotations.SQLRestriction;

@Entity
@Getter
@Setter
@SQLDelete(sql = "UPDATE orders SET is_deleted = true WHERE id=?")
@SQLRestriction(value = "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;

@Column(nullable = false)
private BigDecimal total;

@Column(nullable = false)
private LocalDateTime orderDate;

@Column(nullable = false)
private String shippingAddress;

@OneToMany(mappedBy = "order", cascade = CascadeType.ALL, orphanRemoval = true)
private Set<OrderItem> orderItems;

private boolean isDeleted = false;

public enum Status {
PENDING,
CONFIRMED,
DELIVERED,
}
}
38 changes: 38 additions & 0 deletions src/main/java/mate/academy/bookshop/model/OrderItem.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
package mate.academy.bookshop.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;

@Entity
@Getter
@Setter
@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;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package mate.academy.bookshop.repository;

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

public interface OrderItemRepository extends JpaRepository<OrderItem, Long> {
List<OrderItem> findAllByOrder(Order order);

Optional<OrderItem> findByIdAndOrderIdAndOrderUserId(Long orderItemId,
Long orderId,
Long userId);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package mate.academy.bookshop.repository;

import java.util.List;
import java.util.Optional;
import mate.academy.bookshop.model.Order;
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", "orderItems.book"})
List<Order> findAllByUserId(Pageable pageable, Long userId);

@EntityGraph(attributePaths = {"orderItems", "orderItems.book"})
Optional<Order> findByIdAndUserId(Long orderId, Long userId);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package mate.academy.bookshop.service.order;

import java.util.List;
import mate.academy.bookshop.dto.order.OrderRequestDto;
import mate.academy.bookshop.dto.order.OrderResponseDto;
import mate.academy.bookshop.dto.order.OrderUpdateStatusDto;
import mate.academy.bookshop.dto.orderitem.OrderItemResponseDto;
import org.springframework.data.domain.Pageable;

public interface OrderService {
OrderResponseDto createOrderByUserId(Long userId, OrderRequestDto orderRequestDto);

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

OrderResponseDto updateStatusByOrderId(Long orderId,
OrderUpdateStatusDto orderUpdateStatusDto);

List<OrderItemResponseDto> getOrderItemsByOrderId(Long orderId, Long userId);

OrderItemResponseDto getOrderItemFromOrderById(Long orderId,
Long orderItemId,
Long userId);
}
Loading