mvc add searh and favorites table
This commit is contained in:
parent
989809469d
commit
92414d74c8
@ -0,0 +1,5 @@
|
||||
package com.ip.library.controllers.favorites;
|
||||
|
||||
import org.springframework.data.repository.CrudRepository;
|
||||
|
||||
public interface FavoriteRepository extends CrudRepository<FavoriteEntity, UserBookId> {}
|
@ -0,0 +1,108 @@
|
||||
package com.ip.library.controllers.users;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.modelmapper.ModelMapper;
|
||||
import org.springframework.security.core.annotation.AuthenticationPrincipal;
|
||||
import org.springframework.stereotype.Controller;
|
||||
import org.springframework.ui.Model;
|
||||
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.RequestParam;
|
||||
import org.springframework.web.servlet.mvc.support.RedirectAttributes;
|
||||
|
||||
import com.ip.library.controllers.authors.AuthorEntity;
|
||||
import com.ip.library.controllers.books.BookDto;
|
||||
import com.ip.library.controllers.books.BookEntity;
|
||||
import com.ip.library.controllers.books.BookService;
|
||||
import com.ip.library.core.api.PageAttributesMapper;
|
||||
import com.ip.library.core.configuration.Constants;
|
||||
import com.ip.library.core.security.UserPrincipal;
|
||||
|
||||
@Controller
|
||||
public class UserBookController {
|
||||
private static final String BOOK_SEARCH_VIEW = "book-search";
|
||||
private static final String USER_FAVORITES_VIEW = "user-favorites";
|
||||
private static final String PAGE_ATTRIBUTE = "page";
|
||||
|
||||
private final UserService userService;
|
||||
private final BookService bookService;
|
||||
private final ModelMapper modelMapper;
|
||||
|
||||
public UserBookController(
|
||||
UserService userService,
|
||||
BookService bookService,
|
||||
ModelMapper modelMapper) {
|
||||
this.bookService = bookService;
|
||||
this.userService = userService;
|
||||
this.modelMapper = modelMapper;
|
||||
}
|
||||
|
||||
private BookDto toBookDto (BookEntity entity) {
|
||||
BookDto bookDto = modelMapper.map(entity, BookDto.class);
|
||||
List<AuthorEntity> authors = entity.getAuthorsBooks().stream().map(x -> x.getAuthor()).toList();
|
||||
bookDto.setAuthorsId(authors.stream().map(x -> x.getId()).toList());
|
||||
bookDto.setTypeName(entity.getType().getName());
|
||||
StringBuilder authorName = new StringBuilder();
|
||||
for (AuthorEntity authorEntity : authors) {
|
||||
authorName.append(", ").append(authorEntity.getName());
|
||||
}
|
||||
if (authorName.length() > 0) {
|
||||
bookDto.setAuthorName(authorName.toString().substring(2));
|
||||
} else {
|
||||
bookDto.setAuthorName("Неизвестен");
|
||||
}
|
||||
return bookDto;
|
||||
}
|
||||
|
||||
@GetMapping
|
||||
public String getFavorites(
|
||||
@RequestParam(name = PAGE_ATTRIBUTE, defaultValue = "0") int page,
|
||||
Model model,
|
||||
@AuthenticationPrincipal UserPrincipal principal) {
|
||||
final Map<String, Object> attributes = PageAttributesMapper.toAttributes(
|
||||
userService.getUserFavorities(principal.getId(), page, Constants.DEFUALT_PAGE_SIZE),
|
||||
this::toBookDto);
|
||||
model.addAllAttributes(attributes);
|
||||
model.addAttribute(PAGE_ATTRIBUTE, page);
|
||||
return USER_FAVORITES_VIEW;
|
||||
}
|
||||
|
||||
@PostMapping(Constants.API_URL + "/removeFavorite/{id}")
|
||||
public String removeFavorite(
|
||||
@PathVariable(name = "id") Long id,
|
||||
@RequestParam(name = PAGE_ATTRIBUTE, defaultValue = "0") int page,
|
||||
RedirectAttributes redirectAttributes,
|
||||
@AuthenticationPrincipal UserPrincipal principal) {
|
||||
redirectAttributes.addAttribute(PAGE_ATTRIBUTE, page);
|
||||
userService.removeFavorite(principal.getId(), id);
|
||||
return Constants.REDIRECT_VIEW + "/";
|
||||
}
|
||||
|
||||
@GetMapping(Constants.API_URL + "/search")
|
||||
public String getAll(
|
||||
@RequestParam(name = PAGE_ATTRIBUTE, defaultValue = "0") int page,
|
||||
@RequestParam(name = "typeId", defaultValue = "-1") Long typeId,
|
||||
@RequestParam(name = "authorId", defaultValue = "-1") Long authorId,
|
||||
Model model) {
|
||||
final Map<String, Object> attributes = PageAttributesMapper.toAttributes(
|
||||
bookService.getAll(typeId, authorId, page, Constants.DEFUALT_PAGE_SIZE),
|
||||
this::toBookDto);
|
||||
model.addAllAttributes(attributes);
|
||||
model.addAttribute(PAGE_ATTRIBUTE, page);
|
||||
return BOOK_SEARCH_VIEW;
|
||||
}
|
||||
|
||||
@PostMapping(Constants.API_URL + "/addFavorite/{id}")
|
||||
public String addFavorite(
|
||||
@PathVariable(name = "id") Long id,
|
||||
@RequestParam(name = PAGE_ATTRIBUTE, defaultValue = "0") int page,
|
||||
RedirectAttributes redirectAttributes,
|
||||
@AuthenticationPrincipal UserPrincipal principal) {
|
||||
redirectAttributes.addAttribute(PAGE_ATTRIBUTE, page);
|
||||
userService.addFavorite(principal.getId(), id);
|
||||
return Constants.REDIRECT_VIEW + Constants.API_URL + "/search";
|
||||
}
|
||||
}
|
@ -16,27 +16,33 @@ import org.springframework.util.StringUtils;
|
||||
|
||||
import com.ip.library.controllers.books.BookEntity;
|
||||
import com.ip.library.controllers.books.BookService;
|
||||
import com.ip.library.controllers.favorites.FavoriteEntity;
|
||||
import com.ip.library.controllers.favorites.FavoriteRepository;
|
||||
import com.ip.library.controllers.favorites.UserBookId;
|
||||
import com.ip.library.core.configuration.Constants;
|
||||
import com.ip.library.core.error.NotFoundException;
|
||||
import com.ip.library.core.security.UserPrincipal;
|
||||
|
||||
@Service
|
||||
public class UserService implements UserDetailsService{
|
||||
private final UserRepository repository;
|
||||
private final UserRepository userRepository;
|
||||
private final FavoriteRepository favoriteRepository;
|
||||
private final BookService bookService;
|
||||
private final PasswordEncoder passwordEncoder;
|
||||
|
||||
public UserService(
|
||||
UserRepository repository,
|
||||
FavoriteRepository favoriteRepository,
|
||||
BookService bookService,
|
||||
PasswordEncoder passwordEncoder) {
|
||||
this.repository = repository;
|
||||
this.userRepository = repository;
|
||||
this.favoriteRepository = favoriteRepository;
|
||||
this.bookService = bookService;
|
||||
this.passwordEncoder = passwordEncoder;
|
||||
}
|
||||
|
||||
private void checkLoginUniqueness(String name){
|
||||
if (repository.findByLoginIgnoreCase(name).isPresent()) {
|
||||
if (userRepository.findByLoginIgnoreCase(name).isPresent()) {
|
||||
throw new IllegalArgumentException(
|
||||
String.format("Type with name %s already exists", name)
|
||||
);
|
||||
@ -45,23 +51,23 @@ public class UserService implements UserDetailsService{
|
||||
|
||||
@Transactional(readOnly = true)
|
||||
public List<UserEntity> getAll() {
|
||||
return StreamSupport.stream(repository.findAll().spliterator(), false).toList();
|
||||
return StreamSupport.stream(userRepository.findAll().spliterator(), false).toList();
|
||||
}
|
||||
|
||||
@Transactional(readOnly = true)
|
||||
public Page<UserEntity> getAll(int page, int size) {
|
||||
return repository.findAll(PageRequest.of(page, size));
|
||||
return userRepository.findAll(PageRequest.of(page, size));
|
||||
}
|
||||
|
||||
@Transactional(readOnly = true)
|
||||
public UserEntity get(long id) {
|
||||
return repository.findById(id)
|
||||
return userRepository.findById(id)
|
||||
.orElseThrow(() -> new NotFoundException(UserEntity.class, id));
|
||||
}
|
||||
|
||||
@Transactional(readOnly = true)
|
||||
public UserEntity getByLogin(String login) {
|
||||
return repository.findByLoginIgnoreCase(login)
|
||||
return userRepository.findByLoginIgnoreCase(login)
|
||||
.orElseThrow(() -> new IllegalArgumentException("Invalid login"));
|
||||
}
|
||||
|
||||
@ -77,7 +83,7 @@ public class UserService implements UserDetailsService{
|
||||
passwordEncoder.encode(
|
||||
StringUtils.hasText(password.strip()) ? password : Constants.DEFAULT_PASSWORD));
|
||||
entity.setRole(Optional.ofNullable(entity.getRole()).orElse(UserRole.USER));
|
||||
return repository.save(entity);
|
||||
return userRepository.save(entity);
|
||||
}
|
||||
|
||||
@Transactional
|
||||
@ -85,13 +91,13 @@ public class UserService implements UserDetailsService{
|
||||
final UserEntity existsEntity = get(id);
|
||||
checkLoginUniqueness(entity.getLogin());
|
||||
existsEntity.setLogin(entity.getLogin());
|
||||
return repository.save(existsEntity);
|
||||
return userRepository.save(existsEntity);
|
||||
}
|
||||
|
||||
@Transactional
|
||||
public UserEntity delete(long id) {
|
||||
final UserEntity existsEntity = get(id);
|
||||
repository.delete(existsEntity);
|
||||
userRepository.delete(existsEntity);
|
||||
return existsEntity;
|
||||
}
|
||||
|
||||
@ -99,21 +105,21 @@ public class UserService implements UserDetailsService{
|
||||
public UserEntity giveAdminRole(long id) {
|
||||
final UserEntity existsEntity = get(id);
|
||||
existsEntity.setRole(UserRole.ADMIN);
|
||||
return repository.save(existsEntity);
|
||||
return userRepository.save(existsEntity);
|
||||
}
|
||||
|
||||
@Transactional
|
||||
public UserEntity giveUserRole(long id) {
|
||||
final UserEntity existsEntity = get(id);
|
||||
existsEntity.setRole(UserRole.USER);
|
||||
return repository.save(existsEntity);
|
||||
return userRepository.save(existsEntity);
|
||||
}
|
||||
|
||||
@Transactional
|
||||
public UserEntity changePassword(long id, String newPassword) {
|
||||
final UserEntity existsEntity = get(id);
|
||||
existsEntity.setPassword(newPassword);
|
||||
return repository.save(existsEntity);
|
||||
return userRepository.save(existsEntity);
|
||||
}
|
||||
|
||||
@Transactional
|
||||
@ -123,14 +129,22 @@ public class UserService implements UserDetailsService{
|
||||
return existsUser.addBook(book);
|
||||
}
|
||||
|
||||
@Transactional
|
||||
public FavoriteEntity removeFavorite(long userId, long bookId) {
|
||||
final FavoriteEntity existsEntity = favoriteRepository.findById(new UserBookId(userId, bookId))
|
||||
.orElseThrow(() -> new IllegalArgumentException("Invalid id"));
|
||||
favoriteRepository.delete(existsEntity);
|
||||
return existsEntity;
|
||||
}
|
||||
|
||||
@Transactional(readOnly = true)
|
||||
public List<BookEntity> getUserFavorities (long userId) {
|
||||
return repository.getUserFavorities(userId);
|
||||
return userRepository.getUserFavorities(userId);
|
||||
}
|
||||
|
||||
@Transactional(readOnly = true)
|
||||
public Page<BookEntity> getUserFavorities (long userId, int page, int size) {
|
||||
return repository.getUserFavorities(userId, PageRequest.of(page, size));
|
||||
return userRepository.getUserFavorities(userId, PageRequest.of(page, size));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -0,0 +1,49 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="ru" xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout" layout:decorate="~{default}">
|
||||
|
||||
<head>
|
||||
<title>Поиск</title>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<main layout:fragment="content">
|
||||
<th:block th:switch="${items.size()}">
|
||||
<h2 th:case="0">Данные отсутствуют</h2>
|
||||
<th:block th:case="*">
|
||||
<h2>Поиск</h2>
|
||||
<table class="table">
|
||||
<caption></caption>
|
||||
<thead>
|
||||
<tr>
|
||||
<th scope="col" class="w-10">ID</th>
|
||||
<th scope="col" class="w-auto">Название</th>
|
||||
<th scope="col" class="w-auto">Жанр</th>
|
||||
<th scope="col" class="w-auto">Автор</th>
|
||||
<th scope="col" class="w-10"></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr th:each="book : ${items}">
|
||||
<th scope="row" th:text="${book.id}"></th>
|
||||
<td th:text="${book.name}"></td>
|
||||
<td th:text="${book.typeName}"></td>
|
||||
<td th:text="${book.authorName}"></td>
|
||||
<td>
|
||||
<form th:action="@{/api/1.0/addFavorite/{id}(id=${book.id})}" method="post">
|
||||
<input type="hidden" th:name="page" th:value="${page}">
|
||||
<button type="submit" class="btn btn-link button-link">В избранное</button>
|
||||
</form>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</th:block>
|
||||
<th:block th:replace="~{ pagination :: pagination (
|
||||
url=${'api/1.0/search'},
|
||||
totalPages=${totalPages},
|
||||
currentPage=${currentPage}) }" />
|
||||
</th:block>
|
||||
</main>
|
||||
</body>
|
||||
|
||||
</html>
|
@ -28,6 +28,14 @@
|
||||
</button>
|
||||
<div class="collapse navbar-collapse" id="main-navbar">
|
||||
<ul class="navbar-nav me-auto link" th:with="activeLink=${#objects.nullSafe(servletPath, '')}">
|
||||
<a class="nav-link" href="/api/1.0/search"
|
||||
th:classappend="${activeLink.startsWith('/api/1.0/search') ? 'active' : ''}">
|
||||
Поиск
|
||||
</a>
|
||||
<a class="nav-link" href="/"
|
||||
th:classappend="${activeLink.startsWith('/api/1.0/search') ? 'active' : ''}">
|
||||
Избранное
|
||||
</a>
|
||||
<th:block sec:authorize="hasRole('ADMIN')">
|
||||
<a class="nav-link" href="/api/1.0/user"
|
||||
th:classappend="${activeLink.startsWith('/api/1.0/user') ? 'active' : ''}">
|
||||
|
@ -0,0 +1,49 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="ru" xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout" layout:decorate="~{default}">
|
||||
|
||||
<head>
|
||||
<title>Избранное</title>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<main layout:fragment="content">
|
||||
<th:block th:switch="${items.size()}">
|
||||
<h2 th:case="0">Данные отсутствуют</h2>
|
||||
<th:block th:case="*">
|
||||
<h2>Избранное</h2>
|
||||
<table class="table">
|
||||
<caption></caption>
|
||||
<thead>
|
||||
<tr>
|
||||
<th scope="col" class="w-10">ID</th>
|
||||
<th scope="col" class="w-auto">Название</th>
|
||||
<th scope="col" class="w-auto">Жанр</th>
|
||||
<th scope="col" class="w-auto">Автор</th>
|
||||
<th scope="col" class="w-10"></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr th:each="book : ${items}">
|
||||
<th scope="row" th:text="${book.id}"></th>
|
||||
<td th:text="${book.name}"></td>
|
||||
<td th:text="${book.typeName}"></td>
|
||||
<td th:text="${book.authorName}"></td>
|
||||
<td>
|
||||
<form th:action="@{/api/1.0/removeFavorite/{id}(id=${book.id})}" method="post">
|
||||
<input type="hidden" th:name="page" th:value="${page}">
|
||||
<button type="submit" class="btn btn-link button-link">Удалить</button>
|
||||
</form>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</th:block>
|
||||
<th:block th:replace="~{ pagination :: pagination (
|
||||
url=${''},
|
||||
totalPages=${totalPages},
|
||||
currentPage=${currentPage}) }" />
|
||||
</th:block>
|
||||
</main>
|
||||
</body>
|
||||
|
||||
</html>
|
Loading…
Reference in New Issue
Block a user