Лаба готова

This commit is contained in:
maxnes3 2023-05-14 19:58:26 +04:00
parent 46732e4a2a
commit cb2828e0a9
15 changed files with 477 additions and 14 deletions

View File

@ -22,4 +22,9 @@ public class AuthorDto {
public String getFirstname(){return firstname;} public String getFirstname(){return firstname;}
public String getLastname(){return lastname;} public String getLastname(){return lastname;}
public String getPhoto(){return photo;} public String getPhoto(){return photo;}
public void setId(Long id){this.id = id;}
public void setFirstname(String firstname){this.firstname = firstname;}
public void setLastname(String lastname){this.lastname = lastname;}
public void setPhoto(String photo){this.photo = photo;}
} }

View File

@ -3,17 +3,22 @@ import org.springframework.stereotype.Controller;
import org.springframework.ui.Model; import org.springframework.ui.Model;
import org.springframework.validation.BindingResult; import org.springframework.validation.BindingResult;
import org.springframework.web.bind.annotation.*; import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import ru.ip.labworks.labworks.bookshop.service.AuthorService; import ru.ip.labworks.labworks.bookshop.service.AuthorService;
import ru.ip.labworks.labworks.bookshop.service.BookService;
import java.io.IOException; import java.io.IOException;
import java.util.Base64;
@Controller @Controller
@RequestMapping("/author") @RequestMapping("/author")
public class AuthorMvcController { public class AuthorMvcController {
private final AuthorService authorService; private final AuthorService authorService;
public AuthorMvcController(AuthorService authorService) private final BookService bookService;
public AuthorMvcController(AuthorService authorService, BookService bookService)
{ {
this.authorService = authorService; this.authorService = authorService;
this.bookService = bookService;
} }
@GetMapping @GetMapping
@ -39,14 +44,16 @@ public class AuthorMvcController {
@PostMapping(value = {"/", "/{id}"}) @PostMapping(value = {"/", "/{id}"})
public String saveAuthor(@PathVariable(required = false) Long id, public String saveAuthor(@PathVariable(required = false) Long id,
@ModelAttribute("authorDto") AuthorDto authorDto, @RequestParam(value = "multipartFile") MultipartFile multipartFile,
BindingResult bindingResult, @ModelAttribute("authorDto") AuthorDto authorDto,
Model model) throws IOException { BindingResult bindingResult,
Model model) throws IOException {
if (bindingResult.hasErrors()) { if (bindingResult.hasErrors()) {
model.addAttribute("errors", model.addAttribute("errors",
bindingResult.getAllErrors()); bindingResult.getAllErrors());
return "author-update"; return "author-update";
} }
authorDto.setPhoto("data:" + multipartFile.getContentType() + ";base64," + Base64.getEncoder().encodeToString(multipartFile.getBytes()));
if (id == null || id <= 0) { if (id == null || id <= 0) {
authorService.addAuthor(authorDto); authorService.addAuthor(authorDto);
} else { } else {
@ -60,4 +67,31 @@ public class AuthorMvcController {
authorService.deleteAuthor(id); authorService.deleteAuthor(id);
return "redirect:/author"; return "redirect:/author";
} }
@GetMapping("/{id}/books")
public String getAuthorBooks(@PathVariable Long id, Model model){
model.addAttribute("author",
new AuthorDto(authorService.findAuthor(id)));
model.addAttribute("authorbooks",
authorService.authorBooks(id).stream()
.map(BookDto::new)
.toList());
model.addAttribute("books",
bookService.findAllBooks().stream()
.map(BookDto::new)
.toList());
return "author-mtm";
}
@PostMapping("/{id}/books")
public String addBookToAuthor(@PathVariable Long id, @RequestParam(value = "bookid") Long bookid){
authorService.addBookToAuthor(id, bookid);
return "redirect:/author/" + id.toString() + "/books";
}
@PostMapping("/{id}/books/{bookid}")
public String removeBookFromAuthor(@PathVariable Long id, @PathVariable Long bookid){
authorService.removeBookFromAuthor(id, bookid);
return "redirect:/author/" + id.toString() + "/books";
}
} }

View File

@ -23,4 +23,9 @@ public class BookDto {
public String getName(){return name;} public String getName(){return name;}
public Date getRelease(){return release;} public Date getRelease(){return release;}
public String getCover(){return cover;} public String getCover(){return cover;}
public void setId(Long id){this.id = id;}
public void setName(String name){this.name = name;}
public void setRelease(Date release){this.release = release;}
public void setCover(String cover){this.cover = cover;}
} }

View File

@ -3,16 +3,21 @@ import org.springframework.stereotype.Controller;
import org.springframework.ui.Model; import org.springframework.ui.Model;
import org.springframework.validation.BindingResult; import org.springframework.validation.BindingResult;
import org.springframework.web.bind.annotation.*; import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import ru.ip.labworks.labworks.bookshop.service.BookService; import ru.ip.labworks.labworks.bookshop.service.BookService;
import ru.ip.labworks.labworks.bookshop.service.GenreService;
import java.io.IOException; import java.io.IOException;
import java.util.Base64;
@Controller @Controller
@RequestMapping("/book") @RequestMapping("/book")
public class BookMvcController { public class BookMvcController {
private final BookService bookService; private final BookService bookService;
public BookMvcController(BookService bookService){ private final GenreService genreService;
public BookMvcController(BookService bookService, GenreService genreService){
this.bookService = bookService; this.bookService = bookService;
this.genreService = genreService;
} }
@GetMapping @GetMapping
@ -38,14 +43,16 @@ public class BookMvcController {
@PostMapping(value = {"/", "/{id}"}) @PostMapping(value = {"/", "/{id}"})
public String saveBook(@PathVariable(required = false) Long id, public String saveBook(@PathVariable(required = false) Long id,
@ModelAttribute("bookDto") BookDto bookDto, @RequestParam(value = "multipartFile") MultipartFile multipartFile,
BindingResult bindingResult, @ModelAttribute("bookDto") BookDto bookDto,
Model model) throws IOException { BindingResult bindingResult,
Model model) throws IOException {
if (bindingResult.hasErrors()) { if (bindingResult.hasErrors()) {
model.addAttribute("errors", model.addAttribute("errors",
bindingResult.getAllErrors()); bindingResult.getAllErrors());
return "book-update"; return "book-update";
} }
bookDto.setCover("data:" + multipartFile.getContentType() + ";base64," + Base64.getEncoder().encodeToString(multipartFile.getBytes()));
if (id == null || id <= 0) { if (id == null || id <= 0) {
bookService.addBook(bookDto); bookService.addBook(bookDto);
} else { } else {
@ -59,4 +66,31 @@ public class BookMvcController {
bookService.deleteBook(id); bookService.deleteBook(id);
return "redirect:/book"; return "redirect:/book";
} }
@GetMapping("/{id}/genres")
public String getBookGenres(@PathVariable Long id, Model model){
model.addAttribute("book",
new BookDto(bookService.findBook(id)));
model.addAttribute("bookgenres",
bookService.bookGenres(id).stream()
.map(GenreDto::new)
.toList());
model.addAttribute("genres",
genreService.findAllGenres().stream()
.map(GenreDto::new)
.toList());
return "book-mtm";
}
@PostMapping("/{id}/genres")
public String addGenreToBook(@PathVariable Long id, @RequestParam(value = "genreid") Long genreid){
bookService.addGenreToBook(id, genreid);
return "redirect:/book/" + id.toString() + "/genres";
}
@PostMapping("/{id}/genres/{genreid}")
public String removeGenreFromBook(@PathVariable Long id, @PathVariable Long genreid){
bookService.removeGenreFromBook(id, genreid);
return "redirect:/book/" + id.toString() + "/genres";
}
} }

View File

@ -14,4 +14,7 @@ public class GenreDto {
public Long getId(){return id;} public Long getId(){return id;}
public String getName(){return name;} public String getName(){return name;}
public void setId(Long id){this.id = id;}
public void setName(String name){this.name = name;}
} }

View File

@ -56,7 +56,7 @@ public class GenreMvcController {
} }
@PostMapping("/delete/{id}") @PostMapping("/delete/{id}")
public String deleteBook(@PathVariable Long id) { public String deleteGenre(@PathVariable Long id) {
genreService.deleteGenre(id); genreService.deleteGenre(id);
return "redirect:/genre"; return "redirect:/genre";
} }

View File

@ -0,0 +1,54 @@
<!DOCTYPE html>
<html lang="en"
xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout"
layout:decorate="~{default}">
<head>
</head>
<body>
<div layout:fragment="content">
<div class="table-responsive">
<table class="table">
<thead>
<tr>
<th scope="col">Название</th>
<th scope="col">Дата релиза</th>
<th scope="col">Фото</th>
<th scope="col">Редактировать запись</th>
</tr>
</thead>
<tbody>
<tr th:each="ab, iterator: ${authorbooks}">
<td th:text="${ab.name}" style="width: 25%"/>
<td th:text="${ab.release}" style="width: 25%"/>
<td><img th:src="${ab.cover}" class="img-thumbnail mw-50 mh-50"/></td>
<td style="width: 10%">
<div class="btn-group" role="group" aria-label="Basic example">
<button type="button" class="btn btn-danger button-fixed button-sm"
th:attr="onclick=|confirm('Удалить запись?') && document.getElementById('remove-${ab.id}').click()|">
<i class="fa fa-trash" aria-hidden="true"></i> Удалить
</button>
</div>
<form th:action="@{/author/{id}/books/{bookid}(id=${author.id}, bookid=${ab.id})}" method="post">
<button th:id="'remove-' + ${ab.id}" type="submit" style="display: none">
Удалить
</button>
</form>
</td>
</tr>
</tbody>
</table>
</div>
<form th:action="@{/author/{id}/books(id=${author.id})}" enctype="bookid/form-data" method="post">
<div class="input-group mb-3">
<select class="form-select" th:name="bookid">
<option th:each="book, iterator: ${books}" th:value="${book.id}" th:text="${book.name}"></option>
</select>
<button class="btn btn-outline-secondary" type="submit">Добавить</button>
</div>
</form>
<div>
<a class="btn btn-secondary" th:href="@{/author}">Закрыть</a>
</div>
</div>
</body>
</html>

View File

@ -0,0 +1,40 @@
<!DOCTYPE html>
<html lang="en"
xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout"
layout:decorate="~{default}">
<head>
</head>
<body>
<div layout:fragment="content">
<div th:text="${errors}" class="margin-bottom alert-danger"></div>
<form action="#" th:action="@{/author/{id}(id=${id})}" th:object="${authorDto}" method="post" enctype="multipart/form-data">
<div class="mb-3">
<label for="firstname" class="form-label">Имя</label>
<input type="text" class="form-control" id="firstname" th:field="${authorDto.firstname}" required="true">
</div>
<div class="mb-3">
<label for="lastname" class="form-label">Фамилия</label>
<input type="text" class="form-control" id="lastname" th:field="${authorDto.lastname}" required="true">
</div>
<div class="mb-3">
<label for="photo" class="form-label">Фото</label>
<input type="file" class="form-control" id="photo" th:name="multipartFile" required="true">
<img th:src="${authorDto.photo}" class="img-thumbnail mw-50 mh-50"/>
</div>
<div class="mb-3">
<button type="submit" class="btn btn-success button-fixed">
<span th:if="${id == null}">
<i class="fa-solid fa-plus"></i> Добавить
</span>
<span th:if="${id != null}">
<i class="fa fa-pencil" aria-hidden="true"></i> Сохранить
</span>
</button>
<a class="btn btn-secondary button-fixed" th:href="@{/author}">
Назад
</a>
</div>
</form>
</div>
</body>
</html>

View File

@ -0,0 +1,57 @@
<!DOCTYPE html>
<html lang="en"
xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout"
layout:decorate="~{default}">
<head>
</head>
<body>
<div layout:fragment="content">
<div>
<a class="btn btn-success button-fixed"
th:href="@{/author/update/}">
<i class="fa-solid fa-plus"></i> Добавить
</a>
</div>
<div class="table-responsive">
<table class="table">
<thead>
<tr>
<th scope="col">Имя</th>
<th scope="col">Фамилия</th>
<th scope="col">Фото</th>
<th scope="col">Редактировать запись</th>
</tr>
</thead>
<tbody>
<tr th:each="author, iterator: ${authors}">
<td th:text="${author.firstname}" style="width: 25%"/>
<td th:text="${author.lastname}" style="width: 25%"/>
<td><img th:src="${author.photo}" class="img-thumbnail mw-50 mh-50"/></td>
<td style="width: 10%">
<div class="btn-group" role="group" aria-label="Basic example">
<a class="btn btn-warning button-fixed button-sm"
th:href="@{/author/update/{id}(id=${author.id})}">
<i class="fa fa-pencil" aria-hidden="true"></i> Изменить
</a>
<button type="button" class="btn btn-danger button-fixed button-sm"
th:attr="onclick=|confirm('Удалить запись?') && document.getElementById('remove-${author.id}').click()|">
<i class="fa fa-trash" aria-hidden="true"></i> Удалить
</button>
<a class="btn btn-primary button-fixed button-sm"
th:href="@{/author/{id}/books(id=${author.id})}">
<i class="fa fa-pencil" aria-hidden="true"></i> Книга
</a>
</div>
<form th:action="@{/author/delete/{id}(id=${author.id})}" method="post">
<button th:id="'remove-' + ${author.id}" type="submit" style="display: none">
Удалить
</button>
</form>
</td>
</tr>
</tbody>
</table>
</div>
</div>
</body>
</html>

View File

@ -0,0 +1,50 @@
<!DOCTYPE html>
<html lang="en"
xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout"
layout:decorate="~{default}">
<head>
</head>
<body>
<div layout:fragment="content">
<div class="table-responsive">
<table class="table">
<thead>
<tr>
<th scope="col">Название</th>
<th scope="col">Редактировать запись</th>
</tr>
</thead>
<tbody>
<tr th:each="bg, iterator: ${bookgenres}">
<td th:text="${bg.name}" style="width: 25%"/>
<td style="width: 10%">
<div class="btn-group" role="group" aria-label="Basic example">
<button type="button" class="btn btn-danger button-fixed button-sm"
th:attr="onclick=|confirm('Удалить запись?') && document.getElementById('remove-${bg.id}').click()|">
<i class="fa fa-trash" aria-hidden="true"></i> Удалить
</button>
</div>
<form th:action="@{/book/{id}/genres/{genreid}(id=${book.id}, genreid=${bg.id})}" method="post">
<button th:id="'remove-' + ${bg.id}" type="submit" style="display: none">
Удалить
</button>
</form>
</td>
</tr>
</tbody>
</table>
</div>
<form th:action="@{/book/{id}/genres(id=${book.id})}" enctype="genreid/form-data" method="post">
<div class="input-group mb-3">
<select class="form-select" th:name="genreid">
<option th:each="genre, iterator: ${genres}" th:value="${genre.id}" th:text="${genre.name}"></option>
</select>
<button class="btn btn-outline-secondary" type="submit">Добавить</button>
</div>
</form>
<div>
<a class="btn btn-secondary" th:href="@{/book}">Закрыть</a>
</div>
</div>
</body>
</html>

View File

@ -0,0 +1,40 @@
<!DOCTYPE html>
<html lang="en"
xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout"
layout:decorate="~{default}">
<head>
</head>
<body>
<div layout:fragment="content">
<div th:text="${errors}" class="margin-bottom alert-danger"></div>
<form action="#" th:action="@{/book/{id}(id=${id})}" th:object="${bookDto}" method="post" enctype="multipart/form-data">
<div class="mb-3">
<label for="name" class="form-label">Название</label>
<input type="text" class="form-control" id="name" th:field="${bookDto.name}" required="true">
</div>
<div class="mb-3">
<label for="release" class="form-label">Дата релиза</label>
<input type="date" class="form-control" id="release" th:field="${bookDto.release}" required="true">
</div>
<div class="mb-3">
<label for="cover" class="form-label">Обложка</label>
<input type="file" class="form-control" id="cover" th:name="multipartFile" required="true">
<img th:src="${bookDto.cover}" class="img-thumbnail mw-50 mh-50"/>
</div>
<div class="mb-3">
<button type="submit" class="btn btn-success button-fixed">
<span th:if="${id == null}">
<i class="fa-solid fa-plus"></i> Добавить
</span>
<span th:if="${id != null}">
<i class="fa fa-pencil" aria-hidden="true"></i> Сохранить
</span>
</button>
<a class="btn btn-secondary button-fixed" th:href="@{/book}">
Назад
</a>
</div>
</form>
</div>
</body>
</html>

View File

@ -0,0 +1,57 @@
<!DOCTYPE html>
<html lang="en"
xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout"
layout:decorate="~{default}">
<head>
</head>
<body>
<div layout:fragment="content">
<div>
<a class="btn btn-success button-fixed"
th:href="@{/book/update/}">
<i class="fa-solid fa-plus"></i> Добавить
</a>
</div>
<div class="table-responsive">
<table class="table">
<thead>
<tr>
<th scope="col">Название</th>
<th scope="col">Дата релиза</th>
<th scope="col">Фото</th>
<th scope="col">Редактировать запись</th>
</tr>
</thead>
<tbody>
<tr th:each="book, iterator: ${books}">
<td th:text="${book.name}" style="width: 25%"/>
<td th:text="${book.release}" style="width: 25%"/>
<td><img th:src="${book.cover}" class="img-thumbnail mw-50 mh-50"/></td>
<td style="width: 10%">
<div class="btn-group" role="group" aria-label="Basic example">
<a class="btn btn-warning button-fixed button-sm"
th:href="@{/book/update/{id}(id=${book.id})}">
<i class="fa fa-pencil" aria-hidden="true"></i> Изменить
</a>
<button type="button" class="btn btn-danger button-fixed button-sm"
th:attr="onclick=|confirm('Удалить запись?') && document.getElementById('remove-${book.id}').click()|">
<i class="fa fa-trash" aria-hidden="true"></i> Удалить
</button>
<a class="btn btn-primary button-fixed button-sm"
th:href="@{/book/{id}/genres(id=${book.id})}">
<i class="fa fa-pencil" aria-hidden="true"></i> Жанры
</a>
</div>
<form th:action="@{/book/delete/{id}(id=${book.id})}" method="post">
<button th:id="'remove-' + ${book.id}" type="submit" style="display: none">
Удалить
</button>
</form>
</td>
</tr>
</tbody>
</table>
</div>
</div>
</body>
</html>

View File

@ -16,21 +16,25 @@
<nav class="navbar navbar-expand-lg bg-success" data-bs-theme="dark"> <nav class="navbar navbar-expand-lg bg-success" data-bs-theme="dark">
<div class="container"> <div class="container">
<a class="navbar-brand" href="#"> <a class="navbar-brand" href="#">
<strong>{ OBS } Online Book Service</strong> <strong class="text-white">{ OBS } Online Book Service</strong>
</a> </a>
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarNavAltMarkup" aria-controls="navbarNavAltMarkup" aria-expanded="false" aria-label="Toggle navigation"> <button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarNavAltMarkup" aria-controls="navbarNavAltMarkup" aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span> <span class="navbar-toggler-icon"></span>
</button> </button>
<div class="collapse navbar-collapse" id="navbarNavAltMarkup"> <div class="collapse navbar-collapse" id="navbarNavAltMarkup">
<div class="navbar-nav"> <div class="navbar-nav" th:with="activeLink=${#request.requestURI}">
<a class="nav-link active" aria-current="page" href="/authors">Authors</a> <a class="nav-link active text-white" aria-current="page" href="/author" th:classappend="${#strings.equals(activeLink, '/author')} ? 'active' : ''">Authors</a>
<a class="nav-link active" href="/books">Books</a> <a class="nav-link active text-white" href="/book" th:classappend="${#strings.equals(activeLink, '/book')} ? 'active' : ''">Books</a>
<a class="nav-link active" href="/genres">Genres</a> <a class="nav-link active text-white" href="/genre" th:classappend="${#strings.equals(activeLink, '/genre')} ? 'active' : ''">Genres</a>
</div> </div>
</div> </div>
</div> </div>
</nav> </nav>
</header> </header>
<div class="container-fluid" style="margin-top: 50pt;">
<div class="container container-padding" layout:fragment="content">
</div>
</div>
<footer class="container pt-4 my-md-5 pt-md-5 text-center border-top"> <footer class="container pt-4 my-md-5 pt-md-5 text-center border-top">
<div class="row"> <div class="row">
<div class="col-12 col-md"> <div class="col-12 col-md">

View File

@ -0,0 +1,31 @@
<!DOCTYPE html>
<html lang="en"
xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout"
layout:decorate="~{default}">
<head>
</head>
<body>
<div layout:fragment="content">
<div th:text="${errors}" class="margin-bottom alert-danger"></div>
<form action="#" th:action="@{/genre/{id}(id=${id})}" th:object="${genreDto}" method="post">
<div class="mb-3">
<label for="name" class="form-label">Название</label>
<input type="text" class="form-control" id="name" th:field="${genreDto.name}" required="true">
</div>
<div class="mb-3">
<button type="submit" class="btn btn-success button-fixed">
<span th:if="${id == null}">
<i class="fa-solid fa-plus"></i> Добавить
</span>
<span th:if="${id != null}">
<i class="fa fa-pencil" aria-hidden="true"></i> Сохранить
</span>
</button>
<a class="btn btn-secondary button-fixed" th:href="@{/genre}">
Назад
</a>
</div>
</form>
</div>
</body>
</html>

View File

@ -0,0 +1,49 @@
<!DOCTYPE html>
<html lang="en"
xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout"
layout:decorate="~{default}">
<head>
</head>
<body>
<div layout:fragment="content">
<div>
<a class="btn btn-success button-fixed"
th:href="@{/genre/update/}">
<i class="fa-solid fa-plus"></i> Добавить
</a>
</div>
<div class="table-responsive">
<table class="table">
<thead>
<tr>
<th scope="col">Название</th>
<th scope="col">Редактировать запись</th>
</tr>
</thead>
<tbody>
<tr th:each="genre, iterator: ${genres}">
<td th:text="${genre.name}" style="width: 60%"/>
<td style="width: 10%">
<div class="btn-group" role="group" aria-label="Basic example">
<a class="btn btn-warning button-fixed button-sm"
th:href="@{/genre/update/{id}(id=${genre.id})}">
<i class="fa fa-pencil" aria-hidden="true"></i> Изменить
</a>
<button type="button" class="btn btn-danger button-fixed button-sm"
th:attr="onclick=|confirm('Удалить запись?') && document.getElementById('remove-${genre.id}').click()|">
<i class="fa fa-trash" aria-hidden="true"></i> Удалить
</button>
</div>
<form th:action="@{/genre/delete/{id}(id=${genre.id})}" method="post">
<button th:id="'remove-' + ${genre.id}" type="submit" style="display: none">
Удалить
</button>
</form>
</td>
</tr>
</tbody>
</table>
</div>
</div>
</body>
</html>