From 6a41a7ec0811c8dbf6f88d740f57fa49d397e4ee Mon Sep 17 00:00:00 2001 From: trokhim03 Date: Mon, 3 Mar 2025 11:09:00 +0200 Subject: [PATCH 1/2] added a shoppingCart model --- .../controller/ShoppingCartController.java | 85 +++++++++++++++ .../dto/cartitem/CartItemRequestDto.java | 15 +++ .../dto/cartitem/CartItemResponseDto.java | 14 +++ .../dto/cartitem/CartItemUpdateDto.java | 6 ++ .../shoppingcart/ShoppingCartResponseDto.java | 14 +++ .../academy/bookshop/mapper/BookMapper.java | 9 ++ .../bookshop/mapper/CartItemMapper.java | 26 +++++ .../bookshop/mapper/ShoppingCartMapper.java | 15 +++ .../mate/academy/bookshop/model/Book.java | 6 ++ .../mate/academy/bookshop/model/CartItem.java | 34 ++++++ .../academy/bookshop/model/ShoppingCart.java | 39 +++++++ .../repository/CartItemRepository.java | 9 ++ .../repository/ShoppingCartRepository.java | 12 +++ .../shoppingcart/ShoppingCartService.java | 21 ++++ .../shoppingcart/ShoppingCartServiceImpl.java | 101 ++++++++++++++++++ .../service/user/UserServiceImpl.java | 5 + .../10-create-shopping-carts-table.yaml | 29 +++++ .../changes/11-create-cart-items-table.yaml | 34 ++++++ .../changes/12-insert-shopping-carts.yaml | 23 ++++ .../db/changelog/db.changelog-master.yaml | 6 ++ 20 files changed, 503 insertions(+) create mode 100644 src/main/java/mate/academy/bookshop/controller/ShoppingCartController.java create mode 100644 src/main/java/mate/academy/bookshop/dto/cartitem/CartItemRequestDto.java create mode 100644 src/main/java/mate/academy/bookshop/dto/cartitem/CartItemResponseDto.java create mode 100644 src/main/java/mate/academy/bookshop/dto/cartitem/CartItemUpdateDto.java create mode 100644 src/main/java/mate/academy/bookshop/dto/shoppingcart/ShoppingCartResponseDto.java create mode 100644 src/main/java/mate/academy/bookshop/mapper/CartItemMapper.java create mode 100644 src/main/java/mate/academy/bookshop/mapper/ShoppingCartMapper.java create mode 100644 src/main/java/mate/academy/bookshop/model/CartItem.java create mode 100644 src/main/java/mate/academy/bookshop/model/ShoppingCart.java create mode 100644 src/main/java/mate/academy/bookshop/repository/CartItemRepository.java create mode 100644 src/main/java/mate/academy/bookshop/repository/ShoppingCartRepository.java create mode 100644 src/main/java/mate/academy/bookshop/service/shoppingcart/ShoppingCartService.java create mode 100644 src/main/java/mate/academy/bookshop/service/shoppingcart/ShoppingCartServiceImpl.java create mode 100644 src/main/resources/db/changelog/changes/10-create-shopping-carts-table.yaml create mode 100644 src/main/resources/db/changelog/changes/11-create-cart-items-table.yaml create mode 100644 src/main/resources/db/changelog/changes/12-insert-shopping-carts.yaml diff --git a/src/main/java/mate/academy/bookshop/controller/ShoppingCartController.java b/src/main/java/mate/academy/bookshop/controller/ShoppingCartController.java new file mode 100644 index 0000000..3c02c24 --- /dev/null +++ b/src/main/java/mate/academy/bookshop/controller/ShoppingCartController.java @@ -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 lombok.RequiredArgsConstructor; +import mate.academy.bookshop.dto.cartitem.CartItemRequestDto; +import mate.academy.bookshop.dto.cartitem.CartItemUpdateDto; +import mate.academy.bookshop.dto.shoppingcart.ShoppingCartResponseDto; +import mate.academy.bookshop.exceptions.EntityNotFoundException; +import mate.academy.bookshop.model.User; +import mate.academy.bookshop.service.shoppingcart.ShoppingCartService; +import org.springframework.http.HttpStatus; +import org.springframework.security.core.Authentication; +import org.springframework.web.bind.annotation.DeleteMapping; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.PutMapping; +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; + +@RestController +@RequiredArgsConstructor +@Tag(name = "Shopping Cart", + description = "Operations for managing shopping cart") +@RequestMapping("/cart") +public class ShoppingCartController { + private final ShoppingCartService shoppingCartService; + + @Operation(summary = "Get the authenticated user's shopping cart", + description = "Retrieves the shopping cart of the currently authenticated user.") + @GetMapping + public ShoppingCartResponseDto getShoppingCart(Authentication authentication) { + Long authenticatedUserId = getAuthenticatedUserId(authentication); + return shoppingCartService.getShoppingCart(authenticatedUserId); + } + + @Operation(summary = "Add an item to the shopping cart", + description = "Adds a new item to the shopping cart for the authenticated user.") + @PostMapping + public ShoppingCartResponseDto addCartItem( + @RequestBody @Valid CartItemRequestDto cartItemRequestDto, + Authentication authentication) { + Long authenticatedUserId = getAuthenticatedUserId(authentication); + return shoppingCartService.addCartItem(cartItemRequestDto, authenticatedUserId); + } + + @ResponseStatus(HttpStatus.OK) + @Operation(summary = "Update an item in the shopping cart", + description = "Updates the details of a specific item" + + " in the shopping cart for the authenticated user.") + @PutMapping("/cart-items/{cartItemId}") + public ShoppingCartResponseDto updateCartItems( + @RequestBody @Valid CartItemUpdateDto cartItemUpdateDto, + @PathVariable Long cartItemId, + Authentication authentication) { + Long authenticatedUserId = getAuthenticatedUserId(authentication); + return shoppingCartService.updateCartItems( + authenticatedUserId, + cartItemId, + cartItemUpdateDto + ); + } + + @ResponseStatus(HttpStatus.NO_CONTENT) + @Operation(summary = "Delete an item from the shopping cart", + description = "Removes a specific item from the shopping cart" + + " for the authenticated user.") + @DeleteMapping("/cart-items/{cartItemId}") + public void deleteCartItem(@PathVariable Long cartItemId, Authentication authentication) { + Long authenticatedUserId = getAuthenticatedUserId(authentication); + shoppingCartService.deleteCartItemFromCart(authenticatedUserId, cartItemId); + } + + private Long getAuthenticatedUserId(Authentication authentication) { + if (authentication == null || !(authentication.getPrincipal() instanceof User)) { + throw new EntityNotFoundException("Authentication failed. User not found."); + } + return ((User) authentication.getPrincipal()).getId(); + } + +} diff --git a/src/main/java/mate/academy/bookshop/dto/cartitem/CartItemRequestDto.java b/src/main/java/mate/academy/bookshop/dto/cartitem/CartItemRequestDto.java new file mode 100644 index 0000000..bd38da9 --- /dev/null +++ b/src/main/java/mate/academy/bookshop/dto/cartitem/CartItemRequestDto.java @@ -0,0 +1,15 @@ +package mate.academy.bookshop.dto.cartitem; + +import jakarta.validation.constraints.Min; +import jakarta.validation.constraints.NotNull; +import lombok.Data; + +@Data +public class CartItemRequestDto { + @NotNull + private Long bookId; + + @Min(value = 1, + message = "Quantity must be at least 1") + private int quantity; +} diff --git a/src/main/java/mate/academy/bookshop/dto/cartitem/CartItemResponseDto.java b/src/main/java/mate/academy/bookshop/dto/cartitem/CartItemResponseDto.java new file mode 100644 index 0000000..95abaa6 --- /dev/null +++ b/src/main/java/mate/academy/bookshop/dto/cartitem/CartItemResponseDto.java @@ -0,0 +1,14 @@ +package mate.academy.bookshop.dto.cartitem; + +import lombok.Data; + +@Data +public class CartItemResponseDto { + private Long id; + + private Long bookId; + + private String bookTitle; + + private int quantity; +} diff --git a/src/main/java/mate/academy/bookshop/dto/cartitem/CartItemUpdateDto.java b/src/main/java/mate/academy/bookshop/dto/cartitem/CartItemUpdateDto.java new file mode 100644 index 0000000..eca959d --- /dev/null +++ b/src/main/java/mate/academy/bookshop/dto/cartitem/CartItemUpdateDto.java @@ -0,0 +1,6 @@ +package mate.academy.bookshop.dto.cartitem; + +import jakarta.validation.constraints.Positive; + +public record CartItemUpdateDto(@Positive int quantity) { +} diff --git a/src/main/java/mate/academy/bookshop/dto/shoppingcart/ShoppingCartResponseDto.java b/src/main/java/mate/academy/bookshop/dto/shoppingcart/ShoppingCartResponseDto.java new file mode 100644 index 0000000..e1e9933 --- /dev/null +++ b/src/main/java/mate/academy/bookshop/dto/shoppingcart/ShoppingCartResponseDto.java @@ -0,0 +1,14 @@ +package mate.academy.bookshop.dto.shoppingcart; + +import java.util.Set; +import lombok.Data; +import mate.academy.bookshop.dto.cartitem.CartItemResponseDto; + +@Data +public class ShoppingCartResponseDto { + private Long id; + + private Long userId; + + private Set cartItemsDto; +} diff --git a/src/main/java/mate/academy/bookshop/mapper/BookMapper.java b/src/main/java/mate/academy/bookshop/mapper/BookMapper.java index 40c9ed6..ef154cb 100644 --- a/src/main/java/mate/academy/bookshop/mapper/BookMapper.java +++ b/src/main/java/mate/academy/bookshop/mapper/BookMapper.java @@ -1,6 +1,7 @@ package mate.academy.bookshop.mapper; import java.util.List; +import java.util.Optional; import java.util.Set; import java.util.stream.Collectors; import mate.academy.bookshop.config.MapperConfig; @@ -13,6 +14,7 @@ import org.mapstruct.BeanMapping; import org.mapstruct.Mapper; import org.mapstruct.MappingTarget; +import org.mapstruct.Named; import org.mapstruct.NullValuePropertyMappingStrategy; @Mapper(config = MapperConfig.class) @@ -47,4 +49,11 @@ default Set mapCategoryIdsToCategories(List categoriesIds) { .map(Category::new) .collect(Collectors.toSet()); } + + @Named("bookById") + default Book bookById(Long id) { + return Optional.ofNullable(id) + .map(Book::new) + .orElse(null); + } } diff --git a/src/main/java/mate/academy/bookshop/mapper/CartItemMapper.java b/src/main/java/mate/academy/bookshop/mapper/CartItemMapper.java new file mode 100644 index 0000000..fe5bc69 --- /dev/null +++ b/src/main/java/mate/academy/bookshop/mapper/CartItemMapper.java @@ -0,0 +1,26 @@ +package mate.academy.bookshop.mapper; + +import mate.academy.bookshop.config.MapperConfig; +import mate.academy.bookshop.dto.cartitem.CartItemRequestDto; +import mate.academy.bookshop.dto.cartitem.CartItemResponseDto; +import mate.academy.bookshop.dto.cartitem.CartItemUpdateDto; +import mate.academy.bookshop.model.CartItem; +import org.mapstruct.Mapper; +import org.mapstruct.Mapping; +import org.mapstruct.MappingTarget; +import org.mapstruct.Named; + +@Mapper(config = MapperConfig.class, uses = BookMapper.class) +public interface CartItemMapper { + @Named("cartItemToDto") + @Mapping(source = "book.id", target = "bookId") + @Mapping(source = "book.title", target = "bookTitle") + CartItemResponseDto toDto(CartItem cartItem); + + @Mapping(target = "book", source = "bookId", qualifiedByName = "bookById") + @Mapping(target = "shoppingCart", ignore = true) + CartItem toEntity(CartItemRequestDto cartItemRequestDto); + + void updateCartItemFromDto(CartItemUpdateDto cartItemUpdateDto, + @MappingTarget CartItem cartItem); +} diff --git a/src/main/java/mate/academy/bookshop/mapper/ShoppingCartMapper.java b/src/main/java/mate/academy/bookshop/mapper/ShoppingCartMapper.java new file mode 100644 index 0000000..ee848ff --- /dev/null +++ b/src/main/java/mate/academy/bookshop/mapper/ShoppingCartMapper.java @@ -0,0 +1,15 @@ +package mate.academy.bookshop.mapper; + +import mate.academy.bookshop.config.MapperConfig; +import mate.academy.bookshop.dto.shoppingcart.ShoppingCartResponseDto; +import mate.academy.bookshop.model.ShoppingCart; +import org.mapstruct.Mapper; +import org.mapstruct.Mapping; + +@Mapper(config = MapperConfig.class, uses = CartItemMapper.class) +public interface ShoppingCartMapper { + @Mapping(source = "user.id", target = "userId") + @Mapping(source = "cartItems", target = "cartItemsDto", qualifiedByName = "cartItemToDto") + ShoppingCartResponseDto toDto(ShoppingCart shoppingCart); + +} diff --git a/src/main/java/mate/academy/bookshop/model/Book.java b/src/main/java/mate/academy/bookshop/model/Book.java index e7b173a..3d7caf8 100644 --- a/src/main/java/mate/academy/bookshop/model/Book.java +++ b/src/main/java/mate/academy/bookshop/model/Book.java @@ -14,6 +14,7 @@ import java.util.Set; import lombok.EqualsAndHashCode; import lombok.Getter; +import lombok.NoArgsConstructor; import lombok.Setter; import lombok.ToString; import org.hibernate.annotations.SQLDelete; @@ -24,6 +25,7 @@ @Setter @SQLDelete(sql = "UPDATE books SET is_deleted = true WHERE id=?") @SQLRestriction(value = "is_deleted = false") +@NoArgsConstructor @Table(name = "books") public class Book { @Id @@ -59,4 +61,8 @@ public class Book { @ToString.Exclude @EqualsAndHashCode.Exclude private Set categories = new HashSet<>(); + + public Book(Long id) { + this.id = id; + } } diff --git a/src/main/java/mate/academy/bookshop/model/CartItem.java b/src/main/java/mate/academy/bookshop/model/CartItem.java new file mode 100644 index 0000000..2730438 --- /dev/null +++ b/src/main/java/mate/academy/bookshop/model/CartItem.java @@ -0,0 +1,34 @@ +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 lombok.Getter; +import lombok.Setter; + +@Entity +@Setter +@Getter +@Table(name = "cart_items") +public class CartItem { + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + + @ManyToOne(fetch = FetchType.LAZY, optional = false) + @JoinColumn(name = "cart_id", nullable = false) + private ShoppingCart shoppingCart; + + @ManyToOne(fetch = FetchType.LAZY, optional = false) + @JoinColumn(name = "book_id", nullable = false) + private Book book; + + @Column(nullable = false) + private int quantity; +} diff --git a/src/main/java/mate/academy/bookshop/model/ShoppingCart.java b/src/main/java/mate/academy/bookshop/model/ShoppingCart.java new file mode 100644 index 0000000..e23bcdb --- /dev/null +++ b/src/main/java/mate/academy/bookshop/model/ShoppingCart.java @@ -0,0 +1,39 @@ +package mate.academy.bookshop.model; + +import jakarta.persistence.CascadeType; +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.OneToMany; +import jakarta.persistence.OneToOne; +import jakarta.persistence.Table; +import java.util.Set; +import lombok.Getter; +import lombok.Setter; +import org.hibernate.annotations.SQLDelete; +import org.hibernate.annotations.SQLRestriction; + +@Entity +@Setter +@Getter +@SQLDelete(sql = "UPDATE shopping_carts SET is_deleted = true WHERE id=?") +@SQLRestriction(value = "is_deleted = false") +@Table(name = "shopping_carts") +public class ShoppingCart { + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + + @OneToOne(fetch = FetchType.LAZY, optional = false) + @JoinColumn(name = "user_id", unique = true) + private User user; + + @OneToMany(mappedBy = "shoppingCart", cascade = CascadeType.ALL, orphanRemoval = true) + private Set cartItems; + + private boolean isDeleted = false; + +} diff --git a/src/main/java/mate/academy/bookshop/repository/CartItemRepository.java b/src/main/java/mate/academy/bookshop/repository/CartItemRepository.java new file mode 100644 index 0000000..2ae3469 --- /dev/null +++ b/src/main/java/mate/academy/bookshop/repository/CartItemRepository.java @@ -0,0 +1,9 @@ +package mate.academy.bookshop.repository; + +import java.util.Optional; +import mate.academy.bookshop.model.CartItem; +import org.springframework.data.jpa.repository.JpaRepository; + +public interface CartItemRepository extends JpaRepository { + Optional findByIdAndShoppingCartId(Long itemId, Long cartId); +} diff --git a/src/main/java/mate/academy/bookshop/repository/ShoppingCartRepository.java b/src/main/java/mate/academy/bookshop/repository/ShoppingCartRepository.java new file mode 100644 index 0000000..3181661 --- /dev/null +++ b/src/main/java/mate/academy/bookshop/repository/ShoppingCartRepository.java @@ -0,0 +1,12 @@ +package mate.academy.bookshop.repository; + +import java.util.Optional; +import mate.academy.bookshop.model.ShoppingCart; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Query; + +public interface ShoppingCartRepository extends JpaRepository { + @Query("SELECT sc FROM ShoppingCart sc LEFT JOIN FETCH sc.cartItems " + + "WHERE sc.user.id = :userId AND sc.user.isDeleted = false") + Optional findByUserId(Long userId); +} diff --git a/src/main/java/mate/academy/bookshop/service/shoppingcart/ShoppingCartService.java b/src/main/java/mate/academy/bookshop/service/shoppingcart/ShoppingCartService.java new file mode 100644 index 0000000..4a985cb --- /dev/null +++ b/src/main/java/mate/academy/bookshop/service/shoppingcart/ShoppingCartService.java @@ -0,0 +1,21 @@ +package mate.academy.bookshop.service.shoppingcart; + +import mate.academy.bookshop.dto.cartitem.CartItemRequestDto; +import mate.academy.bookshop.dto.cartitem.CartItemUpdateDto; +import mate.academy.bookshop.dto.shoppingcart.ShoppingCartResponseDto; +import mate.academy.bookshop.model.User; + +public interface ShoppingCartService { + void createShoppingCart(User user); + + ShoppingCartResponseDto getShoppingCart(Long userId); + + ShoppingCartResponseDto addCartItem(CartItemRequestDto cartItemUpdateDto, Long userId); + + ShoppingCartResponseDto updateCartItems(Long userId, + Long cartItemId, + CartItemUpdateDto cartItemUpdateDto + ); + + void deleteCartItemFromCart(Long userId, Long cartItemId); +} diff --git a/src/main/java/mate/academy/bookshop/service/shoppingcart/ShoppingCartServiceImpl.java b/src/main/java/mate/academy/bookshop/service/shoppingcart/ShoppingCartServiceImpl.java new file mode 100644 index 0000000..905222f --- /dev/null +++ b/src/main/java/mate/academy/bookshop/service/shoppingcart/ShoppingCartServiceImpl.java @@ -0,0 +1,101 @@ +package mate.academy.bookshop.service.shoppingcart; + +import jakarta.transaction.Transactional; +import java.util.Optional; +import lombok.RequiredArgsConstructor; +import mate.academy.bookshop.dto.cartitem.CartItemRequestDto; +import mate.academy.bookshop.dto.cartitem.CartItemUpdateDto; +import mate.academy.bookshop.dto.shoppingcart.ShoppingCartResponseDto; +import mate.academy.bookshop.exceptions.EntityNotFoundException; +import mate.academy.bookshop.mapper.CartItemMapper; +import mate.academy.bookshop.mapper.ShoppingCartMapper; +import mate.academy.bookshop.model.CartItem; +import mate.academy.bookshop.model.ShoppingCart; +import mate.academy.bookshop.model.User; +import mate.academy.bookshop.repository.CartItemRepository; +import mate.academy.bookshop.repository.ShoppingCartRepository; +import org.springframework.stereotype.Service; + +@Service +@RequiredArgsConstructor +public class ShoppingCartServiceImpl implements ShoppingCartService { + private final ShoppingCartRepository shoppingCartRepository; + private final CartItemRepository cartItemRepository; + private final ShoppingCartMapper shoppingCartMapper; + private final CartItemMapper cartItemMapper; + + @Override + public void createShoppingCart(User user) { + ShoppingCart shoppingCart = new ShoppingCart(); + shoppingCart.setUser(user); + shoppingCartRepository.save(shoppingCart); + } + + @Override + public ShoppingCartResponseDto getShoppingCart(Long userId) { + ShoppingCart shoppingCartByUserId = getShoppingCartByUserId(userId); + return shoppingCartMapper.toDto(shoppingCartByUserId); + } + + @Override + @Transactional + public ShoppingCartResponseDto addCartItem(CartItemRequestDto cartItemUpdateDto, Long userId) { + ShoppingCart shoppingCartByUserId = getShoppingCartByUserId(userId); + + Optional existingCartItem = shoppingCartByUserId + .getCartItems() + .stream() + .filter(cartItem -> cartItem + .getBook() + .getId() + .equals(cartItemUpdateDto.getBookId())) + .findFirst(); + + if (existingCartItem.isPresent()) { + CartItem cartItem = existingCartItem.get(); + cartItem.setQuantity(cartItem.getQuantity() + cartItemUpdateDto.getQuantity()); + } else { + CartItem cartItem = cartItemMapper.toEntity(cartItemUpdateDto); + cartItem.setShoppingCart(shoppingCartByUserId); + shoppingCartByUserId.getCartItems().add(cartItem); + } + shoppingCartRepository.save(shoppingCartByUserId); + return shoppingCartMapper.toDto(shoppingCartByUserId); + } + + @Override + @Transactional + public ShoppingCartResponseDto updateCartItems(Long userId, + Long cartItemId, + CartItemUpdateDto updateDto) { + ShoppingCart shoppingCartByUserId = getShoppingCartByUserId(userId); + + CartItem cartItem = cartItemRepository + .findByIdAndShoppingCartId(cartItemId, shoppingCartByUserId.getId()) + .orElseThrow(() -> new EntityNotFoundException("Can't find cart item" + + " with id: " + cartItemId)); + + cartItemMapper.updateCartItemFromDto(updateDto, cartItem); + return shoppingCartMapper.toDto(shoppingCartByUserId); + } + + @Override + @Transactional + public void deleteCartItemFromCart(Long userId, Long cartItemId) { + ShoppingCart shoppingCartByUserId = getShoppingCartByUserId(userId); + + CartItem cartItem = cartItemRepository + .findByIdAndShoppingCartId(cartItemId, shoppingCartByUserId.getId()) + .orElseThrow(() -> new EntityNotFoundException("Can't find cart item" + + " with id: " + cartItemId)); + shoppingCartByUserId.getCartItems().remove(cartItem); + cartItemRepository.delete(cartItem); + } + + private ShoppingCart getShoppingCartByUserId(Long userId) { + ShoppingCart shoppingCart = shoppingCartRepository.findByUserId(userId) + .orElseThrow(() -> new EntityNotFoundException("Can't find shopping cart" + + " by user id: " + userId)); + return shoppingCart; + } +} diff --git a/src/main/java/mate/academy/bookshop/service/user/UserServiceImpl.java b/src/main/java/mate/academy/bookshop/service/user/UserServiceImpl.java index 28a2855..f44f977 100644 --- a/src/main/java/mate/academy/bookshop/service/user/UserServiceImpl.java +++ b/src/main/java/mate/academy/bookshop/service/user/UserServiceImpl.java @@ -12,6 +12,7 @@ import mate.academy.bookshop.model.User; import mate.academy.bookshop.repository.RoleRepository; import mate.academy.bookshop.repository.UserRepository; +import mate.academy.bookshop.service.shoppingcart.ShoppingCartService; import org.springframework.security.crypto.password.PasswordEncoder; import org.springframework.stereotype.Service; @@ -22,6 +23,7 @@ public class UserServiceImpl implements UserService { private final UserMapper userMapper; private final PasswordEncoder passwordEncoder; private final RoleRepository roleRepository; + private final ShoppingCartService shoppingCartService; @Override @Transactional @@ -37,6 +39,9 @@ public UserResponseDto register(UserRegistrationRequestDto userRegistrationReque .orElseThrow(() -> new EntityNotFoundException("Default role not found")); user.setRoles(Set.of(defaultRole)); userRepository.save(user); + + shoppingCartService.createShoppingCart(user); + return userMapper.toUserResponse(user); } } diff --git a/src/main/resources/db/changelog/changes/10-create-shopping-carts-table.yaml b/src/main/resources/db/changelog/changes/10-create-shopping-carts-table.yaml new file mode 100644 index 0000000..141d614 --- /dev/null +++ b/src/main/resources/db/changelog/changes/10-create-shopping-carts-table.yaml @@ -0,0 +1,29 @@ +databaseChangeLog: + - changeSet: + id: create-shopping-carts-table + author: trokhim + changes: + - createTable: + tableName: shopping_carts + columns: + - column: + name: id + type: bigint + autoIncrement: true + constraints: + primaryKey: true + nullable: false + - column: + name: user_id + type: bigint + constraints: + foreignKeyName: fk_shopping_cart_user + referencedTableName: users + referencedColumnNames: id + nullable: false + - column: + name: is_deleted + type: boolean + defaultValueBoolean: false + constraints: + nullable: false diff --git a/src/main/resources/db/changelog/changes/11-create-cart-items-table.yaml b/src/main/resources/db/changelog/changes/11-create-cart-items-table.yaml new file mode 100644 index 0000000..020fbc0 --- /dev/null +++ b/src/main/resources/db/changelog/changes/11-create-cart-items-table.yaml @@ -0,0 +1,34 @@ +databaseChangeLog: + - changeSet: + id: create-cart-items-table + author: trokhim + changes: + - createTable: + tableName: cart_items + columns: + - column: + name: id + type: bigint + autoIncrement: true + constraints: + primaryKey: true + nullable: false + - column: + name: cart_id + type: bigint + constraints: + foreignKeyName: fk_item_shopping_cart + referencedTableName: shopping_carts + referencedColumnNames: id + nullable: false + - column: + name: book_id + type: bigint + constraints: + foreignKeyName: fk_items_book + referencedTableName: books + referencedColumnNames: id + nullable: false + - column: + name: quantity + type: int \ No newline at end of file diff --git a/src/main/resources/db/changelog/changes/12-insert-shopping-carts.yaml b/src/main/resources/db/changelog/changes/12-insert-shopping-carts.yaml new file mode 100644 index 0000000..a14fec2 --- /dev/null +++ b/src/main/resources/db/changelog/changes/12-insert-shopping-carts.yaml @@ -0,0 +1,23 @@ +databaseChangeLog: + - changeSet: + id: insert-shopping-carts + author: trokhim + changes: + - insert: + tableName: shopping_carts + columns: + - column: + name: user_id + value: 1 + - column: + name: is_deleted + value: 0 + - insert: + tableName: shopping_carts + columns: + - column: + name: user_id + value: 2 + - column: + name: is_deleted + value: 0 \ No newline at end of file diff --git a/src/main/resources/db/changelog/db.changelog-master.yaml b/src/main/resources/db/changelog/db.changelog-master.yaml index dcece44..21b6003 100644 --- a/src/main/resources/db/changelog/db.changelog-master.yaml +++ b/src/main/resources/db/changelog/db.changelog-master.yaml @@ -15,3 +15,9 @@ databaseChangeLog: file: db/changelog/changes/08-create-categories-table.yaml - include: file: db/changelog/changes/09-create-books-categories-table.yaml + - include: + file: db/changelog/changes/10-create-shopping-carts-table.yaml + - include: + file: db/changelog/changes/11-create-cart-items-table.yaml + - include: + file: db/changelog/changes/12-insert-shopping-carts.yaml From 9353f2d0e417547c543feb2023498563aa928742 Mon Sep 17 00:00:00 2001 From: trokhim03 Date: Tue, 4 Mar 2025 10:55:01 +0200 Subject: [PATCH 2/2] changed the shopping cart model --- .../bookshop/controller/ShoppingCartController.java | 5 ----- .../bookshop/dto/cartitem/CartItemRequestDto.java | 6 +++--- .../java/mate/academy/bookshop/model/ShoppingCart.java | 5 ++--- .../bookshop/repository/ShoppingCartRepository.java | 5 ++--- .../service/shoppingcart/ShoppingCartServiceImpl.java | 4 +--- .../academy/bookshop/service/user/UserServiceImpl.java | 2 -- .../changes/10-create-shopping-carts-table.yaml | 9 ++------- .../db/changelog/changes/11-create-cart-items-table.yaml | 5 +++-- .../db/changelog/changes/12-insert-shopping-carts.yaml | 2 +- 9 files changed, 14 insertions(+), 29 deletions(-) diff --git a/src/main/java/mate/academy/bookshop/controller/ShoppingCartController.java b/src/main/java/mate/academy/bookshop/controller/ShoppingCartController.java index 3c02c24..6d4f1e3 100644 --- a/src/main/java/mate/academy/bookshop/controller/ShoppingCartController.java +++ b/src/main/java/mate/academy/bookshop/controller/ShoppingCartController.java @@ -7,7 +7,6 @@ import mate.academy.bookshop.dto.cartitem.CartItemRequestDto; import mate.academy.bookshop.dto.cartitem.CartItemUpdateDto; import mate.academy.bookshop.dto.shoppingcart.ShoppingCartResponseDto; -import mate.academy.bookshop.exceptions.EntityNotFoundException; import mate.academy.bookshop.model.User; import mate.academy.bookshop.service.shoppingcart.ShoppingCartService; import org.springframework.http.HttpStatus; @@ -76,10 +75,6 @@ public void deleteCartItem(@PathVariable Long cartItemId, Authentication authent } private Long getAuthenticatedUserId(Authentication authentication) { - if (authentication == null || !(authentication.getPrincipal() instanceof User)) { - throw new EntityNotFoundException("Authentication failed. User not found."); - } return ((User) authentication.getPrincipal()).getId(); } - } diff --git a/src/main/java/mate/academy/bookshop/dto/cartitem/CartItemRequestDto.java b/src/main/java/mate/academy/bookshop/dto/cartitem/CartItemRequestDto.java index bd38da9..0d62330 100644 --- a/src/main/java/mate/academy/bookshop/dto/cartitem/CartItemRequestDto.java +++ b/src/main/java/mate/academy/bookshop/dto/cartitem/CartItemRequestDto.java @@ -1,15 +1,15 @@ package mate.academy.bookshop.dto.cartitem; -import jakarta.validation.constraints.Min; import jakarta.validation.constraints.NotNull; +import jakarta.validation.constraints.Positive; import lombok.Data; @Data public class CartItemRequestDto { @NotNull + @Positive private Long bookId; - @Min(value = 1, - message = "Quantity must be at least 1") + @Positive private int quantity; } diff --git a/src/main/java/mate/academy/bookshop/model/ShoppingCart.java b/src/main/java/mate/academy/bookshop/model/ShoppingCart.java index e23bcdb..60b35c0 100644 --- a/src/main/java/mate/academy/bookshop/model/ShoppingCart.java +++ b/src/main/java/mate/academy/bookshop/model/ShoppingCart.java @@ -3,10 +3,9 @@ import jakarta.persistence.CascadeType; 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.MapsId; import jakarta.persistence.OneToMany; import jakarta.persistence.OneToOne; import jakarta.persistence.Table; @@ -24,9 +23,9 @@ @Table(name = "shopping_carts") public class ShoppingCart { @Id - @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; + @MapsId @OneToOne(fetch = FetchType.LAZY, optional = false) @JoinColumn(name = "user_id", unique = true) private User user; diff --git a/src/main/java/mate/academy/bookshop/repository/ShoppingCartRepository.java b/src/main/java/mate/academy/bookshop/repository/ShoppingCartRepository.java index 3181661..a1b8076 100644 --- a/src/main/java/mate/academy/bookshop/repository/ShoppingCartRepository.java +++ b/src/main/java/mate/academy/bookshop/repository/ShoppingCartRepository.java @@ -2,11 +2,10 @@ import java.util.Optional; import mate.academy.bookshop.model.ShoppingCart; +import org.springframework.data.jpa.repository.EntityGraph; import org.springframework.data.jpa.repository.JpaRepository; -import org.springframework.data.jpa.repository.Query; public interface ShoppingCartRepository extends JpaRepository { - @Query("SELECT sc FROM ShoppingCart sc LEFT JOIN FETCH sc.cartItems " - + "WHERE sc.user.id = :userId AND sc.user.isDeleted = false") + @EntityGraph(attributePaths = {"cartItems", "cartItems.book"}) Optional findByUserId(Long userId); } diff --git a/src/main/java/mate/academy/bookshop/service/shoppingcart/ShoppingCartServiceImpl.java b/src/main/java/mate/academy/bookshop/service/shoppingcart/ShoppingCartServiceImpl.java index 905222f..660abb7 100644 --- a/src/main/java/mate/academy/bookshop/service/shoppingcart/ShoppingCartServiceImpl.java +++ b/src/main/java/mate/academy/bookshop/service/shoppingcart/ShoppingCartServiceImpl.java @@ -17,6 +17,7 @@ import org.springframework.stereotype.Service; @Service +@Transactional @RequiredArgsConstructor public class ShoppingCartServiceImpl implements ShoppingCartService { private final ShoppingCartRepository shoppingCartRepository; @@ -38,7 +39,6 @@ public ShoppingCartResponseDto getShoppingCart(Long userId) { } @Override - @Transactional public ShoppingCartResponseDto addCartItem(CartItemRequestDto cartItemUpdateDto, Long userId) { ShoppingCart shoppingCartByUserId = getShoppingCartByUserId(userId); @@ -64,7 +64,6 @@ public ShoppingCartResponseDto addCartItem(CartItemRequestDto cartItemUpdateDto, } @Override - @Transactional public ShoppingCartResponseDto updateCartItems(Long userId, Long cartItemId, CartItemUpdateDto updateDto) { @@ -80,7 +79,6 @@ public ShoppingCartResponseDto updateCartItems(Long userId, } @Override - @Transactional public void deleteCartItemFromCart(Long userId, Long cartItemId) { ShoppingCart shoppingCartByUserId = getShoppingCartByUserId(userId); diff --git a/src/main/java/mate/academy/bookshop/service/user/UserServiceImpl.java b/src/main/java/mate/academy/bookshop/service/user/UserServiceImpl.java index f44f977..f33648f 100644 --- a/src/main/java/mate/academy/bookshop/service/user/UserServiceImpl.java +++ b/src/main/java/mate/academy/bookshop/service/user/UserServiceImpl.java @@ -39,9 +39,7 @@ public UserResponseDto register(UserRegistrationRequestDto userRegistrationReque .orElseThrow(() -> new EntityNotFoundException("Default role not found")); user.setRoles(Set.of(defaultRole)); userRepository.save(user); - shoppingCartService.createShoppingCart(user); - return userMapper.toUserResponse(user); } } diff --git a/src/main/resources/db/changelog/changes/10-create-shopping-carts-table.yaml b/src/main/resources/db/changelog/changes/10-create-shopping-carts-table.yaml index 141d614..56dca3f 100644 --- a/src/main/resources/db/changelog/changes/10-create-shopping-carts-table.yaml +++ b/src/main/resources/db/changelog/changes/10-create-shopping-carts-table.yaml @@ -6,21 +6,16 @@ databaseChangeLog: - createTable: tableName: shopping_carts columns: - - column: - name: id - type: bigint - autoIncrement: true - constraints: - primaryKey: true - nullable: false - column: name: user_id type: bigint constraints: + primaryKey: true foreignKeyName: fk_shopping_cart_user referencedTableName: users referencedColumnNames: id nullable: false + deleteCascade: true - column: name: is_deleted type: boolean diff --git a/src/main/resources/db/changelog/changes/11-create-cart-items-table.yaml b/src/main/resources/db/changelog/changes/11-create-cart-items-table.yaml index 020fbc0..29cc5bc 100644 --- a/src/main/resources/db/changelog/changes/11-create-cart-items-table.yaml +++ b/src/main/resources/db/changelog/changes/11-create-cart-items-table.yaml @@ -19,8 +19,9 @@ databaseChangeLog: constraints: foreignKeyName: fk_item_shopping_cart referencedTableName: shopping_carts - referencedColumnNames: id + referencedColumnNames: user_id nullable: false + deleteCascade: true - column: name: book_id type: bigint @@ -31,4 +32,4 @@ databaseChangeLog: nullable: false - column: name: quantity - type: int \ No newline at end of file + type: int diff --git a/src/main/resources/db/changelog/changes/12-insert-shopping-carts.yaml b/src/main/resources/db/changelog/changes/12-insert-shopping-carts.yaml index a14fec2..86dd6d3 100644 --- a/src/main/resources/db/changelog/changes/12-insert-shopping-carts.yaml +++ b/src/main/resources/db/changelog/changes/12-insert-shopping-carts.yaml @@ -20,4 +20,4 @@ databaseChangeLog: value: 2 - column: name: is_deleted - value: 0 \ No newline at end of file + value: 0