From f48ede3cf4cc754c0d5c20465ccb591a575c007f Mon Sep 17 00:00:00 2001 From: lcaohoanq Date: Fri, 13 Dec 2024 09:30:36 +0700 Subject: [PATCH] feat: add get cart by user, add status cart item column --- .../lcaohoanq/shoppe/domain/cart/Cart.java | 2 + .../shoppe/domain/cart/CartController.java | 32 +++++++++++++- .../shoppe/domain/cart/CartItem.java | 13 +++++- .../shoppe/domain/cart/CartItemResponse.java | 14 +++++- .../shoppe/domain/cart/CartItemService.java | 2 + .../shoppe/domain/cart/CartResponse.java | 27 ++++++++---- .../shoppe/domain/cart/CartService.java | 44 ++++++++++++++----- .../shoppe/domain/cart/ICartService.java | 2 + .../shoppe/domain/product/Product.java | 3 ++ .../shoppe/enums/CartItemStatus.java | 13 ++++++ .../lcaohoanq/shoppe/util/DTOConverter.java | 5 ++- client/src/apis/auth.api.ts | 4 +- client/src/contexts/app.context.tsx | 8 ++-- client/src/pages/ProductList/ProductList.tsx | 4 +- 14 files changed, 138 insertions(+), 35 deletions(-) create mode 100644 SPCServer/springboot/src/main/java/com/lcaohoanq/shoppe/enums/CartItemStatus.java diff --git a/SPCServer/springboot/src/main/java/com/lcaohoanq/shoppe/domain/cart/Cart.java b/SPCServer/springboot/src/main/java/com/lcaohoanq/shoppe/domain/cart/Cart.java index 64d2a48..9da60b9 100644 --- a/SPCServer/springboot/src/main/java/com/lcaohoanq/shoppe/domain/cart/Cart.java +++ b/SPCServer/springboot/src/main/java/com/lcaohoanq/shoppe/domain/cart/Cart.java @@ -1,6 +1,7 @@ package com.lcaohoanq.shoppe.domain.cart; import com.fasterxml.jackson.annotation.JsonBackReference; +import com.fasterxml.jackson.annotation.JsonManagedReference; import com.fasterxml.jackson.annotation.JsonProperty; import com.lcaohoanq.shoppe.domain.user.User; import com.lcaohoanq.shoppe.base.entity.BaseEntity; @@ -52,6 +53,7 @@ public class Cart extends BaseEntity { private User user; @OneToMany(mappedBy = "cart", cascade = CascadeType.ALL, orphanRemoval = true) + @JsonManagedReference private List cartItems = new ArrayList<>(); public void addCartProduct(CartItem cartItem) { diff --git a/SPCServer/springboot/src/main/java/com/lcaohoanq/shoppe/domain/cart/CartController.java b/SPCServer/springboot/src/main/java/com/lcaohoanq/shoppe/domain/cart/CartController.java index aff7593..ffd52a0 100644 --- a/SPCServer/springboot/src/main/java/com/lcaohoanq/shoppe/domain/cart/CartController.java +++ b/SPCServer/springboot/src/main/java/com/lcaohoanq/shoppe/domain/cart/CartController.java @@ -1,12 +1,14 @@ package com.lcaohoanq.shoppe.domain.cart; import com.lcaohoanq.shoppe.api.ApiResponse; +import com.lcaohoanq.shoppe.api.PageResponse; import com.lcaohoanq.shoppe.domain.user.IUserService; import com.lcaohoanq.shoppe.domain.user.User; import com.lcaohoanq.shoppe.exception.MethodArgumentNotValidException; import jakarta.validation.Valid; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; +import org.springframework.data.domain.PageRequest; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.security.access.prepost.PreAuthorize; @@ -14,6 +16,7 @@ import org.springframework.security.core.userdetails.UserDetails; import org.springframework.validation.BindingResult; 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.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; @@ -28,15 +31,40 @@ public class CartController { private final ICartItemService cartItemService; private final IUserService userService; + private final ICartService cartService; @GetMapping("") - public ResponseEntity> getAll( + @PreAuthorize("hasAnyRole('ROLE_MANAGER', 'ROLE_MEMBER', 'ROLE_STAFF', 'ROLE_SHOP_OWNER')") + public ResponseEntity> getAll( @RequestParam(value = "page", defaultValue = "0") int page, @RequestParam(value = "limit", defaultValue = "10") int limit ) { + return ResponseEntity.ok(cartService.getAllCarts(PageRequest.of(page, limit))); + } + + @GetMapping("/{id}") + @PreAuthorize("hasAnyRole('ROLE_MANAGER', 'ROLE_MEMBER', 'ROLE_STAFF', 'ROLE_SHOP_OWNER')") + public ResponseEntity> getCartById( + @PathVariable(value = "id") Long id + ) { + return ResponseEntity.ok( + ApiResponse.builder() + .message("Get cart by id successfully") + .data(cartService.findById(id)) + .statusCode(HttpStatus.OK.value()) + .isSuccess(true) + .build() + ); + } + + @GetMapping("/users") + @PreAuthorize("hasAnyRole('ROLE_MANAGER', 'ROLE_MEMBER', 'ROLE_STAFF', 'ROLE_SHOP_OWNER')") + public ResponseEntity> getCartByUserId( + @RequestParam(value = "id") Long id + ){ return ResponseEntity.ok( ApiResponse.builder() - .data(null) + .data(cartService.getCartByUserId(id)) .build() ); } diff --git a/SPCServer/springboot/src/main/java/com/lcaohoanq/shoppe/domain/cart/CartItem.java b/SPCServer/springboot/src/main/java/com/lcaohoanq/shoppe/domain/cart/CartItem.java index 15655e1..d23bac4 100644 --- a/SPCServer/springboot/src/main/java/com/lcaohoanq/shoppe/domain/cart/CartItem.java +++ b/SPCServer/springboot/src/main/java/com/lcaohoanq/shoppe/domain/cart/CartItem.java @@ -1,10 +1,14 @@ package com.lcaohoanq.shoppe.domain.cart; +import com.fasterxml.jackson.annotation.JsonBackReference; import com.fasterxml.jackson.annotation.JsonProperty; import com.lcaohoanq.shoppe.domain.product.Product; import com.lcaohoanq.shoppe.base.entity.BaseEntity; +import com.lcaohoanq.shoppe.enums.CartItemStatus; import jakarta.persistence.Column; import jakarta.persistence.Entity; +import jakarta.persistence.EnumType; +import jakarta.persistence.Enumerated; import jakarta.persistence.GeneratedValue; import jakarta.persistence.GenerationType; import jakarta.persistence.Id; @@ -13,7 +17,6 @@ import jakarta.persistence.SequenceGenerator; import jakarta.persistence.Table; import lombok.AllArgsConstructor; -import lombok.Builder; import lombok.Getter; import lombok.NoArgsConstructor; import lombok.Setter; @@ -37,12 +40,18 @@ public class CartItem extends BaseEntity { @ManyToOne @JoinColumn(name = "cart_id") + @JsonBackReference private Cart cart; @ManyToOne @JoinColumn(name = "product_id") + @JsonBackReference private Product product; - + @Column(name = "quantity") private int quantity; + + @Enumerated(EnumType.ORDINAL) + @Column(name = "status", nullable = false) + private CartItemStatus status; } \ No newline at end of file diff --git a/SPCServer/springboot/src/main/java/com/lcaohoanq/shoppe/domain/cart/CartItemResponse.java b/SPCServer/springboot/src/main/java/com/lcaohoanq/shoppe/domain/cart/CartItemResponse.java index 65cc49f..863739d 100644 --- a/SPCServer/springboot/src/main/java/com/lcaohoanq/shoppe/domain/cart/CartItemResponse.java +++ b/SPCServer/springboot/src/main/java/com/lcaohoanq/shoppe/domain/cart/CartItemResponse.java @@ -2,10 +2,21 @@ import com.fasterxml.jackson.annotation.JsonFormat; import com.fasterxml.jackson.annotation.JsonProperty; -import com.lcaohoanq.shoppe.domain.product.Product; +import com.fasterxml.jackson.annotation.JsonPropertyOrder; import com.lcaohoanq.shoppe.domain.product.ProductResponse; +import com.lcaohoanq.shoppe.enums.CartItemStatus; import java.time.LocalDateTime; +@JsonPropertyOrder( + { + "id", + "cart_id", + "product", + "quantity", + "created_at", + "updated_at" + } +) public record CartItemResponse( @JsonProperty("id") Long id, @@ -15,6 +26,7 @@ public record CartItemResponse( ProductResponse product, @JsonProperty("quantity") int quantity, + CartItemStatus status, @JsonProperty("created_at") @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss.SSSSSS") diff --git a/SPCServer/springboot/src/main/java/com/lcaohoanq/shoppe/domain/cart/CartItemService.java b/SPCServer/springboot/src/main/java/com/lcaohoanq/shoppe/domain/cart/CartItemService.java index 6c64572..33b2e63 100644 --- a/SPCServer/springboot/src/main/java/com/lcaohoanq/shoppe/domain/cart/CartItemService.java +++ b/SPCServer/springboot/src/main/java/com/lcaohoanq/shoppe/domain/cart/CartItemService.java @@ -7,6 +7,7 @@ import com.lcaohoanq.shoppe.domain.product.ProductRepository; import com.lcaohoanq.shoppe.domain.product.ProductResponse; import com.lcaohoanq.shoppe.domain.user.IUserService; +import com.lcaohoanq.shoppe.enums.CartItemStatus; import com.lcaohoanq.shoppe.exception.MalformBehaviourException; import com.lcaohoanq.shoppe.util.DTOConverter; import java.util.List; @@ -71,6 +72,7 @@ public CartItemResponse createCartItem(long userId, CartItemDTO cartItemDTO) { cartItem = CartItem.builder() .cart(existedCart) .product(existProduct) + .status(CartItemStatus.IN_CART) .quantity(cartItemDTO.quantity()) .build(); } diff --git a/SPCServer/springboot/src/main/java/com/lcaohoanq/shoppe/domain/cart/CartResponse.java b/SPCServer/springboot/src/main/java/com/lcaohoanq/shoppe/domain/cart/CartResponse.java index 32ed7ab..e94e4e2 100644 --- a/SPCServer/springboot/src/main/java/com/lcaohoanq/shoppe/domain/cart/CartResponse.java +++ b/SPCServer/springboot/src/main/java/com/lcaohoanq/shoppe/domain/cart/CartResponse.java @@ -1,29 +1,40 @@ package com.lcaohoanq.shoppe.domain.cart; import com.fasterxml.jackson.annotation.JsonFormat; -import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonManagedReference; import com.fasterxml.jackson.annotation.JsonProperty; -import jakarta.validation.constraints.NotNull; +import com.fasterxml.jackson.annotation.JsonPropertyOrder; +import com.lcaohoanq.shoppe.domain.user.UserResponse; +import com.lcaohoanq.shoppe.enums.CartItemStatus; +import jakarta.persistence.Column; import java.time.LocalDateTime; import java.util.List; -@JsonInclude(JsonInclude.Include.NON_NULL) +@JsonPropertyOrder({ + "id", + "total_quantity", + "total_price", + "user", + "cart_items", + "created_at", + "updated_at" +}) public record CartResponse( @JsonProperty("id") Long id, - + @JsonProperty("total_quantity") Integer totalQuantity, @JsonProperty("total_price") Double totalPrice, - @JsonProperty("user_id") - @NotNull(message = "User id is required when creating a new cart") - Long userId, + @JsonProperty("user") + UserResponse user, @JsonProperty("cart_items") - List cartItems, + @JsonManagedReference + List cartItems, @JsonProperty("created_at") @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss.SSSSSS") diff --git a/SPCServer/springboot/src/main/java/com/lcaohoanq/shoppe/domain/cart/CartService.java b/SPCServer/springboot/src/main/java/com/lcaohoanq/shoppe/domain/cart/CartService.java index 6abb19b..d09d9ca 100644 --- a/SPCServer/springboot/src/main/java/com/lcaohoanq/shoppe/domain/cart/CartService.java +++ b/SPCServer/springboot/src/main/java/com/lcaohoanq/shoppe/domain/cart/CartService.java @@ -1,12 +1,15 @@ package com.lcaohoanq.shoppe.domain.cart; import com.lcaohoanq.shoppe.api.PageResponse; +import com.lcaohoanq.shoppe.base.exception.DataNotFoundException; import com.lcaohoanq.shoppe.domain.user.IUserService; import com.lcaohoanq.shoppe.domain.user.User; import com.lcaohoanq.shoppe.exception.MalformBehaviourException; import com.lcaohoanq.shoppe.util.DTOConverter; +import com.lcaohoanq.shoppe.util.PaginationConverter; import java.util.ArrayList; import lombok.RequiredArgsConstructor; +import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; import org.springframework.security.core.parameters.P; import org.springframework.stereotype.Service; @@ -14,24 +17,24 @@ @Service @RequiredArgsConstructor -public class CartService implements ICartService, DTOConverter { +public class CartService implements ICartService, DTOConverter, PaginationConverter { private final IUserService userService; private final CartRepository cartRepository; @Override public CartResponse create(long userId) { - - if(!userService.existsById(userId)){ + + if (!userService.existsById(userId)) { throw new MalformBehaviourException("User with id: " + userId + " not found"); } - - if(cartRepository.existsByUserId(userId)){ + + if (cartRepository.existsByUserId(userId)) { throw new MalformBehaviourException("Cart existed for user with id: " + userId); } User existedUser = userService.findUserById(userId); - + Cart newCart = Cart.builder() .user(existedUser) .totalPrice(0) @@ -45,21 +48,29 @@ public CartResponse create(long userId) { .build(); newCart.addCartProduct(cartItem); - + return toCartResponse(cartRepository.save(newCart)); - + } @Override public PageResponse getAllCarts(Pageable pageable) { - return null; + + Page cartPage = cartRepository.findAll(pageable); + + return mapPageResponse( + cartPage, + pageable, + this::toCartResponse, + "Get all carts successfully"); } @Override public CartResponse findById(Long cartId) { return toCartResponse(cartRepository - .findById(cartId) - .orElseThrow(() -> new MalformBehaviourException("Cart with id: " + cartId + " not found"))); + .findById(cartId) + .orElseThrow(() -> new DataNotFoundException( + "Cart with id: " + cartId + " not found"))); } @Override @@ -105,10 +116,19 @@ public Boolean existsById(Long cartId) { @Override @Transactional public void updateQuantity(Long cartId, Integer quantity, boolean isIncrease) { - if(isIncrease){ + if (isIncrease) { cartRepository.increase(cartId, quantity); } else { cartRepository.decrease(cartId, quantity); } } + + @Override + public CartResponse getCartByUserId(Long userId) { + Cart existedCart = cartRepository + .findByUserId(userId) + .orElseThrow( + () -> new MalformBehaviourException("Cart not found for user with id: " + userId)); + return toCartResponse(existedCart); + } } diff --git a/SPCServer/springboot/src/main/java/com/lcaohoanq/shoppe/domain/cart/ICartService.java b/SPCServer/springboot/src/main/java/com/lcaohoanq/shoppe/domain/cart/ICartService.java index a1070be..3c4f9d5 100644 --- a/SPCServer/springboot/src/main/java/com/lcaohoanq/shoppe/domain/cart/ICartService.java +++ b/SPCServer/springboot/src/main/java/com/lcaohoanq/shoppe/domain/cart/ICartService.java @@ -27,4 +27,6 @@ public interface ICartService { Boolean existsById(Long cartId); void updateQuantity(Long cartId, Integer quantity, boolean isIncrease); + + CartResponse getCartByUserId(Long userId); } diff --git a/SPCServer/springboot/src/main/java/com/lcaohoanq/shoppe/domain/product/Product.java b/SPCServer/springboot/src/main/java/com/lcaohoanq/shoppe/domain/product/Product.java index 5c70f5a..3586e85 100644 --- a/SPCServer/springboot/src/main/java/com/lcaohoanq/shoppe/domain/product/Product.java +++ b/SPCServer/springboot/src/main/java/com/lcaohoanq/shoppe/domain/product/Product.java @@ -1,6 +1,7 @@ package com.lcaohoanq.shoppe.domain.product; import com.fasterxml.jackson.annotation.JsonBackReference; +import com.fasterxml.jackson.annotation.JsonManagedReference; import com.fasterxml.jackson.annotation.JsonProperty; import com.lcaohoanq.shoppe.domain.cart.CartItem; import com.lcaohoanq.shoppe.domain.category.Category; @@ -78,9 +79,11 @@ public class Product extends BaseEntity { private Warehouse warehouse; @OneToMany(mappedBy = "product") + @JsonManagedReference private List images = new ArrayList<>(); @OneToMany(mappedBy = "product") + @JsonManagedReference private List cartItems = new ArrayList<>(); } diff --git a/SPCServer/springboot/src/main/java/com/lcaohoanq/shoppe/enums/CartItemStatus.java b/SPCServer/springboot/src/main/java/com/lcaohoanq/shoppe/enums/CartItemStatus.java new file mode 100644 index 0000000..b43152d --- /dev/null +++ b/SPCServer/springboot/src/main/java/com/lcaohoanq/shoppe/enums/CartItemStatus.java @@ -0,0 +1,13 @@ +package com.lcaohoanq.shoppe.enums; + +public enum CartItemStatus { + + IN_CART, + ALL, + WAIT_FOR_CONFIRMATION, + WAIT_FOR_GETTING, + IN_PROGRESS, + DELIVERED, + CANCELLED, + +} diff --git a/SPCServer/springboot/src/main/java/com/lcaohoanq/shoppe/util/DTOConverter.java b/SPCServer/springboot/src/main/java/com/lcaohoanq/shoppe/util/DTOConverter.java index d12d2cb..bf918cd 100755 --- a/SPCServer/springboot/src/main/java/com/lcaohoanq/shoppe/util/DTOConverter.java +++ b/SPCServer/springboot/src/main/java/com/lcaohoanq/shoppe/util/DTOConverter.java @@ -149,8 +149,8 @@ default CartResponse toCartResponse(Cart cart){ cart.getId(), cart.getTotalQuantity(), cart.getTotalPrice(), - cart.getUser().getId(), - cart.getCartItems(), + toUserResponse(cart.getUser()), + cart.getCartItems().stream().map(this::toCartItemResponse).toList(), cart.getCreatedAt(), cart.getUpdatedAt() ); @@ -162,6 +162,7 @@ default CartItemResponse toCartItemResponse(CartItem cartItem){ cartItem.getCart().getId(), this.toProductResponse(cartItem.getProduct()), cartItem.getQuantity(), + cartItem.getStatus(), cartItem.getCreatedAt(), cartItem.getUpdatedAt() ); diff --git a/client/src/apis/auth.api.ts b/client/src/apis/auth.api.ts index 3853da6..465f22c 100644 --- a/client/src/apis/auth.api.ts +++ b/client/src/apis/auth.api.ts @@ -2,8 +2,8 @@ import { AuthResponse } from 'src/types/auth.type' import http from 'src/utils/http' export const URL_LOGIN = 'auth/login' -export const URL_REGISTER = 'register' -export const URL_LOGOUT = 'logout' +export const URL_REGISTER = 'auth/register' +export const URL_LOGOUT = 'auth/logout' export const URL_REFRESH_TOKEN = 'refresh-access-token' const authApi = { diff --git a/client/src/contexts/app.context.tsx b/client/src/contexts/app.context.tsx index 4cdfa4a..938960d 100644 --- a/client/src/contexts/app.context.tsx +++ b/client/src/contexts/app.context.tsx @@ -1,13 +1,13 @@ import { createContext, useState } from 'react' import { ExtendedPurchase } from 'src/types/purchase.type' -import { User } from 'src/types/user.type' +import { User, UserResponse } from 'src/types/user.type' import { getAccessTokenFromLS, getProfileFromLS } from 'src/utils/auth' interface AppContextInterface { isAuthenticated: boolean setIsAuthenticated: React.Dispatch> - profile: User | null - setProfile: React.Dispatch> + profile: UserResponse | null + setProfile: React.Dispatch> extendedPurchases: ExtendedPurchase[] setExtendedPurchases: React.Dispatch> reset: () => void @@ -36,7 +36,7 @@ export const AppProvider = ({ }) => { const [isAuthenticated, setIsAuthenticated] = useState(defaultValue.isAuthenticated) const [extendedPurchases, setExtendedPurchases] = useState(defaultValue.extendedPurchases) - const [profile, setProfile] = useState(defaultValue.profile) + const [profile, setProfile] = useState(defaultValue.profile) const reset = () => { setIsAuthenticated(false) diff --git a/client/src/pages/ProductList/ProductList.tsx b/client/src/pages/ProductList/ProductList.tsx index d0aa175..4deb10c 100644 --- a/client/src/pages/ProductList/ProductList.tsx +++ b/client/src/pages/ProductList/ProductList.tsx @@ -47,8 +47,8 @@ export default function ProductList() {
- {productsData.data.data.map((product) => ( -
+ {productsData.data.data.map((product, index: number) => ( +
))}