Skip to content
Open
Show file tree
Hide file tree
Changes from 3 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,86 @@
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.Page;
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 Page<OrderDto> 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<OrderItemDto> 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);
}
}
Original file line number Diff line number Diff line change
@@ -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;
}
19 changes: 19 additions & 0 deletions src/main/java/spring/springbootintro/dto/OrderDto.java
Original file line number Diff line number Diff line change
@@ -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<OrderItem> orderItems;
private LocalDateTime orderDate;
private BigDecimal total;
private Status status;

}
10 changes: 10 additions & 0 deletions src/main/java/spring/springbootintro/dto/OrderItemDto.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package spring.springbootintro.dto;

import lombok.Data;

@Data
public class OrderItemDto {
private Long id;
private Long bookId;
private int quantity;
}
Original file line number Diff line number Diff line change
@@ -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;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package spring.springbootintro.exception;

public class OrderProcessingException extends RuntimeException {
public OrderProcessingException(String message) {
super(message);
}
}
18 changes: 18 additions & 0 deletions src/main/java/spring/springbootintro/mapper/OrderItemMapper.java
Original file line number Diff line number Diff line change
@@ -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);

}
14 changes: 14 additions & 0 deletions src/main/java/spring/springbootintro/mapper/OrderMapper.java
Original file line number Diff line number Diff line change
@@ -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);

}
55 changes: 55 additions & 0 deletions src/main/java/spring/springbootintro/model/Order.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
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;
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(fetch = FetchType.LAZY, optional = false)
@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(mappedBy = "order", cascade = CascadeType.ALL, orphanRemoval = true)
private Set<OrderItem> orderItems;

private boolean isDeleted = false;

}
45 changes: 45 additions & 0 deletions src/main/java/spring/springbootintro/model/OrderItem.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
package spring.springbootintro.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;

@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(fetch = FetchType.LAZY, optional = false)
@JoinColumn(name = "order_id")
private Order order;

@ManyToOne
Comment thread
chupa-ilona marked this conversation as resolved.
Outdated
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

add LAZY

@JoinColumn(name = "book_id")
private Book book;

@Column
private int quantity;
Comment thread
chupa-ilona marked this conversation as resolved.

@Column(nullable = false)
private BigDecimal price;

private boolean isDeleted = false;
}
8 changes: 8 additions & 0 deletions src/main/java/spring/springbootintro/model/Status.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package spring.springbootintro.model;

public enum Status {
COMPLETED,
PENDING,
DELIVERED,
CANCELLED
}
Original file line number Diff line number Diff line change
@@ -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<OrderItem, Long> {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package spring.springbootintro.repository;

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;
import spring.springbootintro.model.Order;

@Repository
public interface OrderRepository extends JpaRepository<Order, Long> {

Page<Order> findAllByUserId(Long userId, Pageable pageable);

Optional<Order> findByIdAndUserId(Long orderId, Long userId);
}
23 changes: 23 additions & 0 deletions src/main/java/spring/springbootintro/service/OrderService.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
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;
import spring.springbootintro.dto.OrderItemDto;
import spring.springbootintro.dto.UpdateOrderStatusRequestDto;

public interface OrderService {

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

OrderDto createOrder(Long userId, CreateOrderRequestDto requestDto);

OrderDto updateOrderStatus(Long orderId, UpdateOrderStatusRequestDto orderDto);

List<OrderItemDto> getOrderItems(Long userId, Long orderId);

OrderItemDto getOrderItem(Long userId, Long orderId, Long itemId);

}
Loading
Loading