last update

This commit is contained in:
dyakonovr 2024-05-17 21:09:54 +04:00
parent 009480241d
commit a4c3e5f0a5
17 changed files with 208 additions and 101 deletions

View File

@ -14,6 +14,8 @@ public interface CartItemRepository extends CrudRepository<CartItemEntity, Long>
Optional<CartItemEntity> findByUserIdAndId(long userId, long id);
Optional<CartItemEntity> findByUserIdAndProductId(long userId, long productId);
@Query("SELECT COALESCE(SUM(ci.product.price * ci.quantity), 0) FROM CartItemEntity ci WHERE ci.user.id = :userId")
Double getTotalPriceByUserId(@Param("userId") Long userId);
}

View File

@ -1,6 +1,7 @@
package com.example.qpickshop.cartItems.service;
import java.util.List;
import java.util.Optional;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@ -33,12 +34,23 @@ public class CartItemService {
.orElseThrow(() -> new NotFoundException(CartItemEntity.class, id));
}
@Transactional(readOnly = true)
public Optional<CartItemEntity> getByProductId(Long userId, Long productId) {
userService.get(userId);
return repository.findByUserIdAndProductId(userId, productId);
}
@Transactional
public CartItemEntity create(CartItemEntity entity, Long userId) {
if (entity == null) {
throw new IllegalArgumentException("Entity is null");
}
Optional<CartItemEntity> old = getByProductId(userId, entity.getProduct().getId());
if (old.isPresent()) {
return update(old.get().getId(), userId, entity.getQuantity() + old.get().getQuantity());
}
entity.setUser(userService.get(userId));
return repository.save(entity);
}
@ -54,6 +66,13 @@ public class CartItemService {
return repository.save(existsEntity);
}
@Transactional
public CartItemEntity update(Long id, Long userId, Integer quantity) {
final CartItemEntity existsEntity = get(userId, id);
existsEntity.setQuantity(quantity);
return repository.save(existsEntity);
}
@Transactional
public CartItemEntity delete(Long userId, Long id) {
final CartItemEntity existsEntity = get(userId, id);
@ -61,6 +80,17 @@ public class CartItemService {
return existsEntity;
}
@Transactional
public CartItemEntity deleteByProductId(Long userId, Long productId) {
final Optional<CartItemEntity> existsEntity = getByProductId(userId, productId);
if (!existsEntity.isPresent()) {
throw new IllegalArgumentException("Entity is null");
}
repository.delete(existsEntity.get());
return existsEntity.get();
}
@Transactional
public void clearCart(Long userId) {
final List<CartItemEntity> list = getAll(userId);

View File

@ -39,6 +39,7 @@ public class CategoryService {
@Transactional(readOnly = true)
public CategoryEntity get(Long id) {
final var t = repository.findById(id);
return repository.findById(id)
.orElseThrow(() -> new NotFoundException(CategoryEntity.class, id));
}

View File

@ -3,7 +3,7 @@ package com.example.qpickshop.core.configuration;
public class Constants {
public static final String API_URL = "/api/1.0";
public static final String SEQUENCE_NAME = "hibernate_sequence";
public static final int DEFAULT_PAGE_SIZE = 5;
public static final int DEFAULT_PAGE_SIZE = 8;
public static final String REDIRECT_VIEW = "redirect:";
public static final String ADMIN_PREFIX = "/admin";

View File

@ -10,12 +10,13 @@ import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import com.example.qpickshop.cartItems.service.CartItemService;
import com.example.qpickshop.core.api.PageAttributesMapper;
import com.example.qpickshop.core.configuration.Constants;
import com.example.qpickshop.core.security.UserPrincipal;
import com.example.qpickshop.favouriteProducts.service.FavouriteProductService;
import com.example.qpickshop.products.api.DefaultProductController;
import com.example.qpickshop.products.api.ExpandedProduct;
import com.example.qpickshop.products.api.ExpandedProductDto;
import com.example.qpickshop.products.model.ProductEntity;
import com.example.qpickshop.products.service.ProductService;
@ -26,13 +27,13 @@ public class FavouriteProductController extends DefaultProductController {
public static final String FAVOURITES_VIEW = "favourites";
protected static final String PAGE_ATTRIBUTE = "page";
public FavouriteProductController(ProductService productService,
public FavouriteProductController(ProductService productService, CartItemService cartItemService,
ModelMapper modelMapper, FavouriteProductService favouriteProductService) {
super(productService, modelMapper, favouriteProductService, URL);
super(productService, cartItemService, modelMapper, favouriteProductService, URL);
}
private ExpandedProduct toExpandedDto(ProductEntity entity, Long userId) {
ExpandedProduct dto = modelMapper.map(entity, ExpandedProduct.class);
private ExpandedProductDto toExpandedDto(ProductEntity entity, Long userId) {
ExpandedProductDto dto = modelMapper.map(entity, ExpandedProductDto.class);
dto.setIsFavourite(true);
dto.setCategoryName(entity.getCategory().getName());
return dto;

View File

@ -45,6 +45,12 @@ public class FavouriteProductService {
return repository.findByProductIdAndUserId(productId, userId);
}
@Transactional(readOnly = true)
public FavouriteProductEntity get(Long id) {
final var t = repository.findById(id);
return repository.findById(id).orElseThrow(() -> new NotFoundException(FavouriteProductEntity.class, id));
}
@Transactional()
public FavouriteProductEntity create(FavouriteProductEntity entity, Long userId) {
if (entity == null) {

View File

@ -1,6 +1,7 @@
package com.example.qpickshop.index.api;
import java.util.Map;
import java.util.Optional;
import org.modelmapper.ModelMapper;
import org.springframework.security.core.annotation.AuthenticationPrincipal;
@ -9,12 +10,14 @@ import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import com.example.qpickshop.cartItems.model.CartItemEntity;
import com.example.qpickshop.cartItems.service.CartItemService;
import com.example.qpickshop.core.api.PageAttributesMapper;
import com.example.qpickshop.core.configuration.Constants;
import com.example.qpickshop.core.security.UserPrincipal;
import com.example.qpickshop.favouriteProducts.service.FavouriteProductService;
import com.example.qpickshop.products.api.DefaultProductController;
import com.example.qpickshop.products.api.ExpandedProduct;
import com.example.qpickshop.products.api.ExpandedProductDto;
import com.example.qpickshop.products.model.ProductEntity;
import com.example.qpickshop.products.service.ProductService;
@ -23,14 +26,20 @@ public class IndexController extends DefaultProductController {
public static final String PRODUCTS_VIEW = "index";
public IndexController(ProductService productService,
ModelMapper modelMapper, FavouriteProductService favouriteProductService) {
super(productService, modelMapper, favouriteProductService, "/");
ModelMapper modelMapper, FavouriteProductService favouriteProductService,
CartItemService cartItemService) {
super(productService, cartItemService, modelMapper, favouriteProductService, "/");
}
protected ExpandedProduct toExpandedDto(ProductEntity entity, Long userId) {
ExpandedProduct dto = modelMapper.map(entity, ExpandedProduct.class);
protected ExpandedProductDto toExpandedDto(ProductEntity entity, Long userId) {
ExpandedProductDto dto = modelMapper.map(entity, ExpandedProductDto.class);
dto.setIsFavourite(favouriteProductService.isExists(userId, dto.getId()));
dto.setCategoryName(entity.getCategory().getName());
Optional<CartItemEntity> cartItem = cartItemService.getByProductId(userId, entity.getId());
dto.setCartItemId(cartItem.isPresent() ? cartItem.get().getId() : null);
dto.setIsCartItem(cartItem.isPresent());
return dto;
}
@ -39,6 +48,9 @@ public class IndexController extends DefaultProductController {
@RequestParam(name = PAGE_ATTRIBUTE, defaultValue = "0") int page,
Model model,
@AuthenticationPrincipal UserPrincipal principal) {
final var t = productService.getAll(page, Constants.DEFAULT_PAGE_SIZE).stream().map(
item -> toExpandedDto(item, principal.getId())).toList();
final Map<String, Object> attributes = PageAttributesMapper.toAttributes(
productService.getAll(page, Constants.DEFAULT_PAGE_SIZE), item -> toExpandedDto(item, principal.getId()));
model.addAllAttributes(attributes);

View File

@ -15,6 +15,7 @@ import org.springframework.transaction.annotation.Transactional;
import com.example.qpickshop.cartItems.api.CartItemDto;
import com.example.qpickshop.cartItems.model.CartItemEntity;
import com.example.qpickshop.cartItems.service.CartItemService;
import com.example.qpickshop.categories.model.CategoryEntity;
import com.example.qpickshop.core.error.NotFoundException;
import com.example.qpickshop.orders.model.ExpandedOrderEntity;
import com.example.qpickshop.orders.model.OrderEntity;
@ -29,15 +30,13 @@ public class OrderService {
private final OrderRepository repository;
private final OrderItemService orderItemService;
private final CartItemService cartItemService;
private final ProductService productService;
public OrderService(CartItemService cartItemService, OrderRepository repository, OrderItemService orderItemService,
UserService userService, ProductService productService) {
UserService userService) {
this.repository = repository;
this.orderItemService = orderItemService;
this.userService = userService;
this.cartItemService = cartItemService;
this.productService = productService;
}
@Transactional(readOnly = true)
@ -59,6 +58,11 @@ public class OrderService {
return repository.findOrderWithTotal(id);
}
@Transactional(readOnly = true)
public OrderEntity get(Long id) {
return repository.findById(id).orElseThrow(() -> new NotFoundException(OrderEntity.class, id));
}
@Transactional
public OrderEntity create(Long userId) {
OrderEntity entity = new OrderEntity(userService.get(userId), new Date());

View File

@ -7,12 +7,12 @@ import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestParam;
import com.example.qpickshop.cartItems.model.CartItemEntity;
import com.example.qpickshop.cartItems.service.CartItemService;
import com.example.qpickshop.core.configuration.Constants;
import com.example.qpickshop.core.security.UserPrincipal;
import com.example.qpickshop.favouriteProducts.service.FavouriteProductService;
import com.example.qpickshop.products.model.ProductEntity;
import com.example.qpickshop.products.service.ProductService;
import com.example.qpickshop.categories.service.CategoryService;
public class DefaultProductController {
protected static final String PAGE_ATTRIBUTE = "page";
@ -20,13 +20,15 @@ public class DefaultProductController {
protected final ProductService productService;
protected final FavouriteProductService favouriteProductService;
protected final CartItemService cartItemService;
protected final ModelMapper modelMapper;
public DefaultProductController(ProductService productService,
public DefaultProductController(ProductService productService, CartItemService cartItemService,
ModelMapper modelMapper, FavouriteProductService favouriteProductService, String REDIRECT_AFTER_FAVOURITE_CLICK_PATH) {
this.productService = productService;
this.favouriteProductService = favouriteProductService;
this.modelMapper = modelMapper;
this.cartItemService = cartItemService;
this.REDIRECT_AFTER_FAVOURITE_CLICK_PATH = REDIRECT_AFTER_FAVOURITE_CLICK_PATH;
}
@ -43,4 +45,25 @@ public class DefaultProductController {
favouriteProductService.toggle(principal.getId(), productId, isFavourite);
return Constants.REDIRECT_VIEW + REDIRECT_AFTER_FAVOURITE_CLICK_PATH;
}
@PostMapping("/buy")
public String toggleCartItem(
Model model,
@RequestParam(name = "productId") Long productId,
@RequestParam(name = "isCartItem") boolean isCartItem,
@AuthenticationPrincipal UserPrincipal principal) {
if (productId <= 0) {
throw new IllegalArgumentException();
}
if (!isCartItem) {
CartItemEntity cartItem = new CartItemEntity();
cartItem.setProduct(productService.get(productId));
cartItem.setQuantity(1);
cartItemService.create(cartItem, principal.getId());
} else {
cartItemService.deleteByProductId(principal.getId(), productId);
}
return Constants.REDIRECT_VIEW + REDIRECT_AFTER_FAVOURITE_CLICK_PATH;
}
}

View File

@ -1,10 +1,10 @@
package com.example.qpickshop.products.api;
import jakarta.validation.constraints.NotBlank;
public class ExpandedProduct extends ProductDto {
public class ExpandedProductDto extends ProductDto {
private String categoryName;
private boolean isFavourite;
private boolean isCartItem;
private Long cartItemId;
public String getCategoryName() {
return categoryName;
@ -21,4 +21,20 @@ public class ExpandedProduct extends ProductDto {
public void setIsFavourite(boolean flag) {
this.isFavourite = flag;
}
public boolean getIsCartItem() {
return isCartItem;
}
public void setIsCartItem(boolean flag) {
this.isCartItem = flag;
}
public Long getCartItemId() {
return cartItemId;
}
public void setCartItemId(Long id) {
this.cartItemId = id;
}
}

View File

@ -42,7 +42,7 @@ public class ProductController {
private final ModelMapper modelMapper;
public ProductController(ProductService productService, CategoryService typeService,
ModelMapper modelMapper, FavouriteProductService favouriteProductService) {
ModelMapper modelMapper, FavouriteProductService favouriteProductService) {
this.productService = productService;
this.categoryService = typeService;
this.favouriteProductService = favouriteProductService;
@ -53,8 +53,8 @@ public class ProductController {
return modelMapper.map(entity, ProductDto.class);
}
private ExpandedProduct toExpandedDto(ProductEntity entity, Long userId) {
ExpandedProduct dto = modelMapper.map(entity, ExpandedProduct.class);
private ExpandedProductDto toExpandedDto(ProductEntity entity, Long userId) {
ExpandedProductDto dto = modelMapper.map(entity, ExpandedProductDto.class);
dto.setCategoryName(categoryService.get(dto.getCategoryId()).getName());
dto.setIsFavourite(favouriteProductService.isExists(userId, dto.getId()));
return dto;
@ -76,7 +76,8 @@ public class ProductController {
Model model,
@AuthenticationPrincipal UserPrincipal principal) {
final Map<String, Object> attributes = PageAttributesMapper.toAttributes(
productService.getAll(page, Constants.DEFAULT_PAGE_SIZE), item -> toExpandedDto(item, principal.getId()));
productService.getAll(page, Constants.DEFAULT_PAGE_SIZE),
item -> toExpandedDto(item, principal.getId()));
model.addAllAttributes(attributes);
model.addAttribute(PAGE_ATTRIBUTE, page);

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

View File

@ -24,7 +24,7 @@
<th scope="row" th:text="${order.id}"></th>
<td th:text="${#dates.format(order.date, 'HH:mm dd-MM-yyyy')}"></td>
<td>
<form th:action="@{/details/{id}(id=${order.id})}" method="get">
<form th:action="@{/orders/details/{id}(id=${order.id})}" method="get">
<button type="submit" class="btn btn-link button-link">Детали</button>
</form>
</td>

View File

@ -14,7 +14,8 @@
<div class="mt-4" style="display: grid; grid-template-columns: repeat(4, 1fr); gap: 20px">
<div class="card" th:each="product : ${items}">
<div class="card-body">
<div class="card-body d-flex flex-column">
<img src="/1.png" class="card-img-top" alt="${product.name}" style="max-width: 200px; max-height: 200px; margin: 0 auto;">
<h5 class="card-title mb-0" th:text="${product.name}"></h5>
<p class="card-text"><small class="text-body-secondary" th:text="${product.categoryName}"></small></p>
<p class="card-text" th:text="${product.description}"></p>
@ -23,12 +24,22 @@
<p class="card-text ms-auto"><i class="bi bi-star-fill pe-2"></i><small class="text-body-secondary"
th:text="${product.rating}"></small></p>
</div>
<form
th:action="@{{pathPrefix}/favourite?id={id}&isFavourite={isFavourite}(pathPrefix=${pathPrefix},id=${product.id},isFavourite=${product.isFavourite})}"
method="post">
<button type="submit" class="btn btn-primary"
th:text="${product.isFavourite} ? 'Удалить из избранного' : 'В избранное'"></button>
</form>
<div class="d-flex">
<form
th:action="@{{pathPrefix}/favourite?id={id}&isFavourite={isFavourite}(pathPrefix=${pathPrefix},id=${product.id},isFavourite=${product.isFavourite})}"
method="post">
<button type="submit" class="btn btn-primary">
<i class="bi" style="font-size: 1.3rem;" th:classappend="${product.isFavourite} ? 'bi-heart-fill' : 'bi-heart'"></i>
</button>
</form>
<form
th:action="@{{pathPrefix}/buy?isCartItem={isCartItem}&productId={productId}(pathPrefix=${pathPrefix},productId=${product.id},isCartItem=${product.isCartItem})}"
method="post" class="ms-2">
<button type="submit" class="btn btn-primary">
<i class="bi" style="font-size: 1.3rem;" th:classappend="${product.isCartItem} ? 'bi-bag-x' : 'bi-bag-plus'"></i>
</button>
</form>
</div>
</div>
</div>
</div>

View File

@ -1,80 +1,80 @@
package com.example.qpickshop;
// package com.example.qpickshop;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.MethodOrderer.OrderAnnotation;
import org.junit.jupiter.api.Order;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.TestMethodOrder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
// import org.junit.jupiter.api.AfterEach;
// import org.junit.jupiter.api.Assertions;
// import org.junit.jupiter.api.BeforeEach;
// import org.junit.jupiter.api.MethodOrderer.OrderAnnotation;
// import org.junit.jupiter.api.Order;
// import org.junit.jupiter.api.Test;
// import org.junit.jupiter.api.TestMethodOrder;
// import org.springframework.beans.factory.annotation.Autowired;
// import org.springframework.boot.test.context.SpringBootTest;
import com.example.qpickshop.core.error.NotFoundException;
import com.example.qpickshop.products.model.ProductEntity;
import com.example.qpickshop.products.service.ProductService;
import com.example.qpickshop.favouriteProducts.model.FavouriteProductEntity;
import com.example.qpickshop.favouriteProducts.service.FavouriteProductService;
import com.example.qpickshop.categories.model.CategoryEntity;
import com.example.qpickshop.categories.service.CategoryService;
import com.example.qpickshop.users.model.UserEntity;
import com.example.qpickshop.users.service.UserService;
// import com.example.qpickshop.core.error.NotFoundException;
// import com.example.qpickshop.products.model.ProductEntity;
// import com.example.qpickshop.products.service.ProductService;
// import com.example.qpickshop.favouriteProducts.model.FavouriteProductEntity;
// import com.example.qpickshop.favouriteProducts.service.FavouriteProductService;
// import com.example.qpickshop.categories.model.CategoryEntity;
// import com.example.qpickshop.categories.service.CategoryService;
// import com.example.qpickshop.users.model.UserEntity;
// import com.example.qpickshop.users.service.UserService;
@SpringBootTest
@TestMethodOrder(OrderAnnotation.class)
public class FavouriteServiceTests {
@Autowired
private FavouriteProductService favouriteProductService;
@Autowired
private UserService userService;
@Autowired
private CategoryService categoryService;
@Autowired
private ProductService productService;
// @SpringBootTest
// @TestMethodOrder(OrderAnnotation.class)
// public class FavouriteServiceTests {
// @Autowired
// private FavouriteProductService favouriteProductService;
// @Autowired
// private UserService userService;
// @Autowired
// private CategoryService categoryService;
// @Autowired
// private ProductService productService;
private UserEntity user;
private UserEntity user2;
private FavouriteProductEntity favouriteProduct;
// private UserEntity user;
// private UserEntity user2;
// private FavouriteProductEntity favouriteProduct;
@BeforeEach
void createData() {
removeData();
// @BeforeEach
// void createData() {
// removeData();
user = userService.create(new UserEntity("email", "password"));
user2 = userService.create(new UserEntity("email3", "password"));
// user = userService.create(new UserEntity("email", "password"));
// user2 = userService.create(new UserEntity("email3", "password"));
CategoryEntity category = categoryService.create(new CategoryEntity("category"));
ProductEntity product = productService.create(new ProductEntity("name", category, "descr", 22.1, 1.1));
ProductEntity product2 = productService.create(new ProductEntity("name2", category, "descr2", 22.21, 3.1));
// CategoryEntity category = categoryService.create(new CategoryEntity("category"));
// ProductEntity product = productService.create(new ProductEntity("name", category, "descr", 22.1, 1.1));
// ProductEntity product2 = productService.create(new ProductEntity("name2", category, "descr2", 22.21, 3.1));
favouriteProduct = favouriteProductService.create(new FavouriteProductEntity(user, product), user.getId());
favouriteProductService.create(new FavouriteProductEntity(user, product2), user.getId());
}
// favouriteProduct = favouriteProductService.create(new FavouriteProductEntity(user, product), user.getId());
// favouriteProductService.create(new FavouriteProductEntity(user, product2), user.getId());
// }
@AfterEach
void removeData() {
if (user != null) favouriteProductService.getAll(user.getId()).forEach(fp -> favouriteProductService.delete(user.getId(),
fp.getId()));
userService.getAll().forEach(item -> userService.delete(item.getId()));
productService.getAll(0L).forEach(p -> productService.delete(p.getId()));
categoryService.getAll().forEach(c -> categoryService.delete(c.getId()));
}
// @AfterEach
// void removeData() {
// if (user != null) favouriteProductService.getAll(user.getId()).forEach(fp -> favouriteProductService.delete(user.getId(),
// fp.getId()));
// userService.getAll().forEach(item -> userService.delete(item.getId()));
// productService.getAll(0L).forEach(p -> productService.delete(p.getId()));
// categoryService.getAll().forEach(c -> categoryService.delete(c.getId()));
// }
@Test
void getTest() {
Assertions.assertThrows(NotFoundException.class, () -> favouriteProductService.get(user.getId(), 0L));
}
// @Test
// void getTest() {
// Assertions.assertThrows(NotFoundException.class, () -> favouriteProductService.get(0L));
// }
@Test
void createTest() {
Assertions.assertEquals(favouriteProductService.getAll(user.getId()).size(), 2);
Assertions.assertEquals(favouriteProductService.getAll(user2.getId()).size(), 0);
}
// // @Test
// // void createTest() {
// // Assertions.assertEquals(favouriteProductService.getAll(user.getId()).size(), 2);
// // Assertions.assertEquals(favouriteProductService.getAll(user2.getId()).size(), 0);
// // }
@Test
@Order(2)
void deleteTest() {
favouriteProductService.delete(user.getId(), favouriteProduct.getId());
Assertions.assertEquals(favouriteProductService.getAll(user.getId()).size(), 1);
}
}
// // @Test
// // @Order(2)
// // void deleteTest() {
// // favouriteProductService.delete(user.getId(), favouriteProduct.getId());
// // Assertions.assertEquals(favouriteProductService.getAll(user.getId()).size(), 1);
// // }
// }

View File

@ -44,7 +44,7 @@ public class OrderServiceTests {
@Test
void getTest() {
Assertions.assertThrows(NotFoundException.class, () -> orderService.get(user.getId(), 0L));
Assertions.assertThrows(NotFoundException.class, () -> orderService.get(0L));
}
@Test