done
This commit is contained in:
parent
e59d4c9465
commit
ab0b1cb0bf
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@ -29,13 +29,13 @@ repositories {
|
||||
dependencies {
|
||||
implementation 'org.springframework.boot:spring-boot-starter-web'
|
||||
implementation 'org.springframework.boot:spring-boot-starter-validation'
|
||||
implementation 'org.springdoc:springdoc-openapi-starter-webmvc-ui:2.3.0'
|
||||
implementation 'org.modelmapper:modelmapper:3.2.0'
|
||||
|
||||
implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
|
||||
implementation 'com.h2database:h2:2.2.224'
|
||||
|
||||
implementation 'org.springframework.boot:spring-boot-devtools'
|
||||
implementation 'org.springframework.boot:spring-boot-starter-thymeleaf'
|
||||
implementation 'org.springframework.boot:spring-boot-starter-thymeleaf'
|
||||
implementation 'nz.net.ultraq.thymeleaf:thymeleaf-layout-dialect:3.3.0'
|
||||
runtimeOnly 'org.webjars.npm:bootstrap:5.3.3'
|
||||
runtimeOnly 'org.webjars.npm:bootstrap-icons:1.11.3'
|
||||
|
5
build/resources/main/public/css/favicon.svg
Normal file
5
build/resources/main/public/css/favicon.svg
Normal file
@ -0,0 +1,5 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor"
|
||||
class="bi bi-emoji-sunglasses-fill" viewBox="0 0 16 16">
|
||||
<path
|
||||
d="M8 16A8 8 0 1 0 8 0a8 8 0 0 0 0 16M2.31 5.243A1 1 0 0 1 3.28 4H6a1 1 0 0 1 1 1v.116A4.2 4.2 0 0 1 8 5c.35 0 .69.04 1 .116V5a1 1 0 0 1 1-1h2.72a1 1 0 0 1 .97 1.243l-.311 1.242A2 2 0 0 1 11.439 8H11a2 2 0 0 1-1.994-1.839A3 3 0 0 0 8 6c-.393 0-.74.064-1.006.161A2 2 0 0 1 5 8h-.438a2 2 0 0 1-1.94-1.515zM4.969 9.75A3.5 3.5 0 0 0 8 11.5a3.5 3.5 0 0 0 3.032-1.75.5.5 0 1 1 .866.5A4.5 4.5 0 0 1 8 12.5a4.5 4.5 0 0 1-3.898-2.25.5.5 0 0 1 .866-.5z" />
|
||||
</svg>
|
After Width: | Height: | Size: 615 B |
BIN
build/resources/main/public/kola.jpg
Normal file
BIN
build/resources/main/public/kola.jpg
Normal file
Binary file not shown.
After Width: | Height: | Size: 86 KiB |
BIN
build/resources/main/public/pizza.jpg
Normal file
BIN
build/resources/main/public/pizza.jpg
Normal file
Binary file not shown.
After Width: | Height: | Size: 243 KiB |
48
build/resources/main/templates/cart.html
Normal file
48
build/resources/main/templates/cart.html
Normal file
@ -0,0 +1,48 @@
|
||||
<!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" class="w-50 mx-auto py-3">
|
||||
<strong class="flex-fill">Корзина</strong>
|
||||
<div class="d-flex flex-column align-items-center">
|
||||
<div class="card col-12 col-md-8 col-lg-6 w-75 align-items-center" th:each="cartItem : ${cart}">
|
||||
<div class="card-body col-12 p-2 d-flex flex-row align-items-center justify-content-center">
|
||||
<div class="col-5"><img src="/kola.jpg" class="w-75"></div>
|
||||
<div class="col-3">
|
||||
Название: [[${cartItem.productName}]]
|
||||
</div>
|
||||
<div class="col-4">
|
||||
Цена: [[${cartItem.Price}]]
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class=" mb-2 col-12 col-md-8 col-lg-6 d-flex justify-content-end">
|
||||
|
||||
</div>
|
||||
<div class="mb-2 col-12 col-md-8 col-lg-6 d-flex justify-content-center"
|
||||
th:if="${not #lists.isEmpty(cart)}">
|
||||
<form action="#" th:action="@{/cart/save}" method="post">
|
||||
<button type="submit" class="btn btn-primary" onclick="return confirm('Вы уверены?')">
|
||||
Оформить заказ
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
<div class="mb-2 col-12 col-md-8 col-lg-6 d-flex align-items-center">
|
||||
<form action="#" th:action="@{/cart/clear}" method="post">
|
||||
<button type="submit" class="btn btn-danger button-fixed-width"
|
||||
onclick="return confirm('Вы уверены?')">
|
||||
<i class="bi bi-x-lg"></i> Очистить
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
|
||||
</html>
|
72
build/resources/main/templates/catalog.html
Normal file
72
build/resources/main/templates/catalog.html
Normal file
@ -0,0 +1,72 @@
|
||||
<!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" class="w-50 mx-auto main">
|
||||
<div class="tab-content mt-2">
|
||||
<h2 class="text-center">Меню</h2>
|
||||
<div class="row mx-auto">
|
||||
<div class="pt-4 ps-2 text-center" style="font-size: larger;">Обязательно возьми</div>
|
||||
<div><img class="p-2" src="/kola.jpg" width="100%" /></div>
|
||||
</div>
|
||||
<div class="tab-pane container active table-responsive" id="orders">
|
||||
<form th:action="@{/catalog}" method="get" class="row mt-2 w-50 mx-auto pb-3">
|
||||
<div class="pb-3">
|
||||
<input type="hidden" th:name="page" th:value="${page}">
|
||||
<select th:name="typeId" id="typeId" class="form-select">
|
||||
<option selected value="">Фильтр по продукции</option>
|
||||
<option th:each="type : ${types}" th:value="${type.id}" th:selected="${type.id==typeId}">
|
||||
[[${type.name}]]
|
||||
</option>
|
||||
</select>
|
||||
<input type="hidden" th:name="page" th:value="${page}">
|
||||
</div>
|
||||
<button type="submit" class="btn btn-primary">Показать</button>
|
||||
</form>
|
||||
<table class="table">
|
||||
<thead>
|
||||
<th class="w-25"></th>
|
||||
<th>Название</th>
|
||||
<th>Тип</th>
|
||||
<th class="w-auto">Описание</th>
|
||||
<th>Цена</th>
|
||||
<th></th>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr th:each="producte : ${items}">
|
||||
<th scope="row"><img src="/kola.jpg" class="w-100"></th>
|
||||
<th scope="row" th:text="${producte.name}"></th>
|
||||
<th:block th:each="type : ${types}">
|
||||
<th:block th:if="${type.Id} eq ${producte.typeId}">
|
||||
<th scope="row" th:text="${type.name}"></th>
|
||||
</th:block>
|
||||
</th:block>
|
||||
<th scope="row" th:text="${producte.description}"></th>
|
||||
<th scope="row" th:text="${producte.Price}"></th>
|
||||
<th>
|
||||
<form th:action="@{/catalog}" th:object="${order}" method="post">
|
||||
<input type="hidden" th:name="page" th:value="${page}">
|
||||
<select hidden="hidden" th:field="*{product}" class="form-select">
|
||||
<option th:value="${producte.id}"></option>
|
||||
</select>
|
||||
<button type="submit" class="btn btn-link button-link"><i
|
||||
class="bi bi-cart2 d-inline-block align-top me-1 logo"></i></button>
|
||||
</form>
|
||||
</th>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<th:block th:replace="~{ pagination :: pagination (
|
||||
url='catalog',
|
||||
totalPages=${totalPages},
|
||||
currentPage=${currentPage}) }" />
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
</body>
|
||||
|
||||
</html>
|
@ -7,7 +7,7 @@
|
||||
<meta charset="UTF-8" />
|
||||
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title layout:title-pattern="$LAYOUT_TITLE - $CONTENT_TITLE">My shop</title>
|
||||
<title layout:title-pattern="$LAYOUT_TITLE - $CONTENT_TITLE">FasterPizza.ru</title>
|
||||
<script type="text/javascript" src="/webjars/bootstrap/5.3.3/dist/js/bootstrap.bundle.min.js"></script>
|
||||
<link rel="stylesheet" href="/webjars/bootstrap/5.3.3/dist/css/bootstrap.min.css" />
|
||||
<link rel="stylesheet" href="/webjars/bootstrap-icons/1.11.3/font/bootstrap-icons.min.css" />
|
||||
@ -18,8 +18,8 @@
|
||||
<nav class="navbar navbar-expand-md my-navbar" data-bs-theme="dark">
|
||||
<div class="container-fluid">
|
||||
<a class="navbar-brand" href="/">
|
||||
<i class="bi bi-cart2 d-inline-block align-top me-1 logo"></i>
|
||||
Туда сюда и пицца
|
||||
<i class="bi-pizza-slice d-inline-block align-top me-1 logo"></i>
|
||||
БыстроПицца
|
||||
</a>
|
||||
<th:block sec:authorize="isAuthenticated()" th:with="userName=${#authentication.name}">
|
||||
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#main-navbar"
|
||||
@ -35,16 +35,25 @@
|
||||
</a>
|
||||
<a class="nav-link" href="/admin/type"
|
||||
th:classappend="${activeLink.startsWith('/admin/type') ? 'active' : ''}">
|
||||
Типы заказов
|
||||
Типы Продукции
|
||||
</a>
|
||||
<a class="nav-link" href="/admin/subscription"
|
||||
th:classappend="${activeLink.startsWith('/admin/subscription') ? 'active' : ''}">
|
||||
Списки рассылки
|
||||
<a class="nav-link" href="/admin/product"
|
||||
th:classappend="${activeLink.startsWith('/admin/product') ? 'active' : ''}">
|
||||
Продукция
|
||||
</a>
|
||||
<a class="nav-link" href="/admin/product/top"
|
||||
th:classappend="${activeLink.startsWith('/admin/product/top') ? 'active' : ''}">
|
||||
Топ Продуктов
|
||||
</a>
|
||||
<a class="nav-link" href="/h2-console/" target="_blank">Консоль H2</a>
|
||||
</th:block>
|
||||
<a class="nav-link" href="/123" target="_blank">Ошибка 1</a>
|
||||
<a class="nav-link" href="/admin/123" target="_blank">Ошибка 2</a>
|
||||
<a class="nav-link" href="/catalog"
|
||||
th:classappend="${activeLink.startsWith('/catalog') ? 'active' : ''}">
|
||||
Каталог
|
||||
</a>
|
||||
<a class="nav-link" href="/">
|
||||
Заказы
|
||||
</a>
|
||||
</ul>
|
||||
<ul class="navbar-nav" th:if="${not #strings.isEmpty(userName)}">
|
||||
<form th:action="@{/logout}" method="post">
|
||||
@ -64,7 +73,7 @@
|
||||
<main class="container-fluid p-2" layout:fragment="content">
|
||||
</main>
|
||||
<footer class="my-footer mt-auto d-flex flex-shrink-0 justify-content-center align-items-center">
|
||||
Автор, [[${#dates.year(#dates.createNow())}]]
|
||||
Крюков А.И., [[${#dates.year(#dates.createNow())}]]
|
||||
</footer>
|
||||
</body>
|
||||
|
||||
|
37
build/resources/main/templates/error.html
Normal file
37
build/resources/main/templates/error.html
Normal file
@ -0,0 +1,37 @@
|
||||
<!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">
|
||||
<ul class="list-group mb-2">
|
||||
<th:block th:if="${#strings.isEmpty(message)}">
|
||||
<li class="list-group-item">
|
||||
Неизвестная ошибка
|
||||
</li>
|
||||
</th:block>
|
||||
<th:block th:if="${not #strings.isEmpty(message)}">
|
||||
<li class="list-group-item">
|
||||
<strong>Ошибка:</strong> [[${message}]]
|
||||
</li>
|
||||
</th:block>
|
||||
<th:block th:if="${not #strings.isEmpty(url)}">
|
||||
<li class="list-group-item">
|
||||
<strong>Адрес:</strong> [[${url}]]
|
||||
</li>
|
||||
<li class="list-group-item">
|
||||
<strong>Класс исключения:</strong> [[${exception}]]
|
||||
</li>
|
||||
<li class="list-group-item">
|
||||
[[${method}]] ([[${file}]]:[[${line}]])
|
||||
</li>
|
||||
</th:block>
|
||||
</ul>
|
||||
<a class="btn btn-primary button-fixed-width" href="/">На главную</a>
|
||||
</main>
|
||||
</body>
|
||||
|
||||
</html>
|
@ -7,7 +7,7 @@
|
||||
|
||||
<body>
|
||||
<main layout:fragment="content">
|
||||
<form action="#" th:action="@{/login}" method="post">
|
||||
<form action="#" th:action="@{/login}" method="post" class="w-25 mx-auto">
|
||||
<div th:if="${param.error}" class="alert alert-danger">
|
||||
Неверный логин или пароль
|
||||
</div>
|
||||
@ -39,6 +39,4 @@
|
||||
</main>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
|
||||
</html>
|
70
build/resources/main/templates/orders.html
Normal file
70
build/resources/main/templates/orders.html
Normal file
@ -0,0 +1,70 @@
|
||||
<!DOCTYPE html>
|
||||
<html xmlns:th="http://www.thymeleaf.org">
|
||||
|
||||
<body>
|
||||
<th:block th:fragment="orders (items, totalPages, currentPage, products)">
|
||||
<th:block th:switch="${items.size()}">
|
||||
<h2 th:case="0">Данные отсутствуют</h2>
|
||||
<th:block th:case="*" class="w-50 mx-auto">
|
||||
|
||||
<table class="table mt-2">
|
||||
<caption></caption>
|
||||
<thead>
|
||||
<tr>
|
||||
<th scope="col" class="w-10">ID</th>
|
||||
<th scope="col" class="w-auto">Игры</th>
|
||||
<th scope="col" class="w-10"></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr th:each="order : ${items}">
|
||||
<th scope="row" th:text="${order.id}"></th>
|
||||
<td>
|
||||
<table class="table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th scope="col" class="w-10"></th>
|
||||
<th scope="col" class="w-25"></th>
|
||||
<th scope="col" class="w-auto">Название продукции</th>
|
||||
<th scope="col" class="w-10">Цена</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr th:each="productId : ${order.products}">
|
||||
<th:block th:each="product : ${products}">
|
||||
<th:block th:if="${productId} eq ${product.id}">
|
||||
<th scope="row" th:text="${productId}"></th>
|
||||
<th scope="row"><img src="/kola.jpg" class="w-100">
|
||||
</th>
|
||||
<th scope="row" th:text="${product.name}"></th>
|
||||
<th scope="row" th:text="${product.price}"></th>
|
||||
</th:block>
|
||||
</th:block>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</td>
|
||||
<td>
|
||||
<form th:action="@{/delete/{id}(id=${order.id})}" method="post">
|
||||
<input type="hidden" th:name="page" th:value="${page}">
|
||||
<button type="submit" class="btn btn-link button-link"
|
||||
onclick="return confirm('Вы уверены?')">Удалить</button>
|
||||
</form>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</th:block>
|
||||
<th:block th:replace="~{ pagination :: pagination (
|
||||
url='',
|
||||
totalPages=${totalPages},
|
||||
currentPage=${currentPage}) }" />
|
||||
<div class="mb-2 d-flex justify-content-center">
|
||||
<a class="btn btn-primary" href="/cart">Создать заказ</a>
|
||||
</div>
|
||||
</th:block>
|
||||
</th:block>
|
||||
|
||||
</body>
|
||||
|
||||
</html>
|
51
build/resources/main/templates/pagination.html
Normal file
51
build/resources/main/templates/pagination.html
Normal file
@ -0,0 +1,51 @@
|
||||
<!DOCTYPE html>
|
||||
<html xmlns:th="http://www.thymeleaf.org">
|
||||
|
||||
<body>
|
||||
|
||||
<th:block th:fragment="pagination (url, totalPages, currentPage)">
|
||||
<nav th:if="${totalPages > 1}" th:with="
|
||||
maxPage=2,
|
||||
currentPage=${currentPage + 1}">
|
||||
<ul class="pagination justify-content-center"
|
||||
th:with="
|
||||
seqFrom=${currentPage - maxPage < 1 ? 1 : currentPage - maxPage},
|
||||
seqTo=${currentPage + maxPage > totalPages ? totalPages : currentPage + maxPage}">
|
||||
<th:block th:if="${currentPage > maxPage + 1}">
|
||||
<li class="page-item">
|
||||
<a class="page-link" aria-label="Previous" th:href="@{/{url}?page=0(url=${url})}">
|
||||
<span aria-hidden="true">«</span>
|
||||
</a>
|
||||
</li>
|
||||
<li class="page-item disabled">
|
||||
<span class="page-link" aria-label="Previous">
|
||||
<span aria-hidden="true">…</span>
|
||||
</span>
|
||||
</li>
|
||||
</th:block>
|
||||
<li class="page-item" th:each="page : ${#numbers.sequence(seqFrom, seqTo)}"
|
||||
th:classappend="${page == currentPage} ? 'active' : ''">
|
||||
<a class=" page-link" th:href="@{/{url}?page={page}(url=${url},page=${page - 1})}">
|
||||
<span th:text="${page}" />
|
||||
</a>
|
||||
</li>
|
||||
<th:block th:if="${currentPage < totalPages - maxPage}">
|
||||
<li class="page-item disabled">
|
||||
<span class="page-link" aria-label="Previous">
|
||||
<span aria-hidden="true">…</span>
|
||||
</span>
|
||||
</li>
|
||||
<li class="page-item">
|
||||
<a class="page-link" aria-label="Next"
|
||||
th:href="@{/{url}?page={page}(url=${url},page=${totalPages - 1})}">
|
||||
<span aria-hidden="true">»</span>
|
||||
</a>
|
||||
</li>
|
||||
</th:block>
|
||||
</ul>
|
||||
</nav>
|
||||
</th:block>
|
||||
|
||||
</body>
|
||||
|
||||
</html>
|
47
build/resources/main/templates/product-edit.html
Normal file
47
build/resources/main/templates/product-edit.html
Normal file
@ -0,0 +1,47 @@
|
||||
<!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" class="w-50 mx-auto">
|
||||
<form action="#" th:action="@{/admin/product/edit/{id}(id=${product.id})}" th:object="${product}" method="post">
|
||||
<div class="mb-3">
|
||||
<label for="id" class="form-label">ID</label>
|
||||
<input type="text" th:value="*{id}" id="id" class="form-control" readonly disabled>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label for="name" class="form-label">Название</label>
|
||||
<input type="text" th:field="*{name}" id="name" class="form-control">
|
||||
<div th:if="${#fields.hasErrors('name')}" th:errors="*{name}" class="invalid-feedback"></div>
|
||||
</div>
|
||||
<div class="mb-2">
|
||||
<label for="typeId" class="form-label">Тип</label>
|
||||
<select th:field="*{typeId}" id="typeId" class="form-select">
|
||||
<option selected value="">Укажите тип</option>
|
||||
<option th:each="type : ${types}" th:value="${type.id}">[[${type.name}]]</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label for="price" class="form-label">Цена</label>
|
||||
<input type="number" th:field="*{price}" id="price" class="form-control">
|
||||
<div th:if="${#fields.hasErrors('price')}" th:errors="*{price}" class="invalid-feedback"></div>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label for="description" class="form-label">Описание</label>
|
||||
<textarea type="textarea" rows="5" cols="80" th:field="*{description}" id="description"
|
||||
class="form-control"></textarea>
|
||||
<div th:if="${#fields.hasErrors('description')}" th:errors="*{description}" class="invalid-feedback">
|
||||
</div>
|
||||
</div>
|
||||
<div class="mb-3 d-flex flex-row">
|
||||
<button class="btn btn-primary me-2 button-fixed-width" type="submit">Сохранить</button>
|
||||
<a class="btn btn-secondary button-fixed-width" href="/admin/product">Отмена</a>
|
||||
</div>
|
||||
</form>
|
||||
</main>
|
||||
</body>
|
||||
|
||||
</html>
|
55
build/resources/main/templates/product.html
Normal file
55
build/resources/main/templates/product.html
Normal file
@ -0,0 +1,55 @@
|
||||
<!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" class="w-50 mx-auto">
|
||||
<th:block th:switch="${items.size()}">
|
||||
<h2 th:case="0">Данные отсутствуют</h2>
|
||||
<th:block th:case="*">
|
||||
<h2 class="text-center">Меню</h2>
|
||||
<div>
|
||||
<a href="/admin/product/edit/" class="btn btn-primary">Добавить продукцию</a>
|
||||
</div>
|
||||
<table class="table">
|
||||
<caption></caption>
|
||||
<thead>
|
||||
<tr>
|
||||
<th scope="col" class="w-10">ID</th>
|
||||
<th scope="col" class="w-10">Название</th>
|
||||
<th scope="col" class="w-10">Цена</th>
|
||||
<th scope="col" class="w-auto">Описание</th>
|
||||
<th scope="col" class="w-10"></th>
|
||||
<th scope="col" class="w-10"></th>
|
||||
</tr>
|
||||
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr th:each="product : ${items}">
|
||||
<th scope="row" th:text="${product.id}"></th>
|
||||
<td th:text="${product.name}"></td>
|
||||
<td th:text="${product.price}"></td>
|
||||
<td th:text="${product.description}"></td>
|
||||
<td>
|
||||
<form th:action="@{/admin/product/edit/{id}(id=${product.id})}" method="get">
|
||||
<button type="submit" class="btn btn-link button-link">Редактировать</button>
|
||||
</form>
|
||||
</td>
|
||||
<td>
|
||||
<form th:action="@{/admin/product/delete/{id}(id=${product.id})}" method="post">
|
||||
<button type="submit" class="btn btn-link button-link"
|
||||
onclick="return confirm('Вы уверены?')">Удалить</button>
|
||||
</form>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</th:block>
|
||||
</th:block>
|
||||
</main>
|
||||
</body>
|
||||
|
||||
</html>
|
20
build/resources/main/templates/profile.html
Normal file
20
build/resources/main/templates/profile.html
Normal file
@ -0,0 +1,20 @@
|
||||
<!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" class="w-50 mx-auto main">
|
||||
<div class="tab-content mt-2">
|
||||
<h2 class="text-center">Заказы</h2>
|
||||
<div class="tab-pane container active" id="orders">
|
||||
<th:block
|
||||
th:replace="~{ orders :: orders (items=${items}, totalPages=${totalPages}, currentPage=${currentPage}, products=${products})}" />
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
</body>
|
||||
|
||||
</html>
|
@ -6,13 +6,18 @@
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<main layout:fragment="content">
|
||||
<main layout:fragment="content" class="w-25 mx-auto">
|
||||
<form action="#" th:action="@{/signup}" th:object="${user}" method="post">
|
||||
<div class="mb-3">
|
||||
<label for="login" class="form-label">Имя пользователя</label>
|
||||
<input type="text" th:field="*{login}" id="login" class="form-control">
|
||||
<div th:if="${#fields.hasErrors('login')}" th:errors="*{login}" class="invalid-feedback"></div>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label for="email" class="form-label">Почта</label>
|
||||
<input type="email" th:field="*{email}" id="email" class="form-control">
|
||||
<div th:if="${#fields.hasErrors('email')}" th:errors="*{email}" class="invalid-feedback"></div>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label for="password" class="form-label">Пароль</label>
|
||||
<input type="password" th:field="*{password}" id="password" class="form-control">
|
||||
@ -32,6 +37,4 @@
|
||||
</main>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
|
||||
</html>
|
33
build/resources/main/templates/top-products.html
Normal file
33
build/resources/main/templates/top-products.html
Normal file
@ -0,0 +1,33 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="ru" xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout" layout:decorate="~{default}">
|
||||
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>Top 5 Products</title>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<main layout:fragment="content" class="w-25 mx-auto">
|
||||
<h1>Top 5 Most Purchased Products</h1>
|
||||
<table class="table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>ID</th>
|
||||
<th>Name</th>
|
||||
<th>Description</th>
|
||||
<th>Price</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr th:each="product : ${topProducts}">
|
||||
<td th:text="${product.id}"></td>
|
||||
<td th:text="${product.name}"></td>
|
||||
<td th:text="${product.description}"></td>
|
||||
<td th:text="${product.price}"></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</main>
|
||||
</body>
|
||||
|
||||
</html>
|
@ -2,18 +2,18 @@
|
||||
<html lang="ru" xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout" layout:decorate="~{default}">
|
||||
|
||||
<head>
|
||||
<title>Редакторовать тип заказа</title>
|
||||
<title>Редакторовать тип продукции</title>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<main layout:fragment="content">
|
||||
<main layout:fragment="content" class="w-50 mx-auto">
|
||||
<form action="#" th:action="@{/admin/type/edit/{id}(id=${type.id})}" th:object="${type}" method="post">
|
||||
<div class="mb-3">
|
||||
<label for="id" class="form-label">ID</label>
|
||||
<input type="text" th:value="*{id}" id="id" class="form-control" readonly disabled>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label for="name" class="form-label">Тип заказа</label>
|
||||
<label for="name" class="form-label">Тип продукции</label>
|
||||
<input type="text" th:field="*{name}" id="name" class="form-control">
|
||||
<div th:if="${#fields.hasErrors('name')}" th:errors="*{name}" class="invalid-feedback"></div>
|
||||
</div>
|
||||
|
@ -2,24 +2,24 @@
|
||||
<html lang="ru" xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout" layout:decorate="~{default}">
|
||||
|
||||
<head>
|
||||
<title>Типы заказов</title>
|
||||
<title>Типы продукции</title>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<main layout:fragment="content">
|
||||
<main layout:fragment="content" class="w-50 mx-auto">
|
||||
<th:block th:switch="${items.size()}">
|
||||
<h2 th:case="0">Данные отсутствуют</h2>
|
||||
<th:block th:case="*">
|
||||
<h2>Типы заказов</h2>
|
||||
<h2 class="text-center">Типы продукции</h2>
|
||||
<div>
|
||||
<a href="/admin/type/edit/" class="btn btn-primary">Добавить тип заказа</a>
|
||||
<a href="/admin/type/edit/" class="btn btn-primary">Добавить тип продукции</a>
|
||||
</div>
|
||||
<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-10"></th>
|
||||
<th scope="col" class="w-10"></th>
|
||||
</tr>
|
||||
|
34
build/resources/main/templates/user-edit.html
Normal file
34
build/resources/main/templates/user-edit.html
Normal file
@ -0,0 +1,34 @@
|
||||
<!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" class="w-50 mx-auto">
|
||||
<form action="#" th:action="@{/admin/user/edit/{id}(id=${user.id},page=${page})}" th:object="${user}"
|
||||
method="post">
|
||||
<div class="mb-3">
|
||||
<label for="id" class="form-label">ID</label>
|
||||
<input type="text" th:value="*{id}" id="id" class="form-control" readonly disabled>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label for="login" class="form-label">Имя пользователя</label>
|
||||
<input type="text" th:field="*{login}" id="login" class="form-control">
|
||||
<div th:if="${#fields.hasErrors('login')}" th:errors="*{login}" class="invalid-feedback"></div>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label for="email" class="form-label">Почта</label>
|
||||
<input type="text" th:field="*{email}" id="email" class="form-control">
|
||||
<div th:if="${#fields.hasErrors('email')}" th:errors="*{email}" class="invalid-feedback"></div>
|
||||
</div>
|
||||
<div class="mb-3 d-flex flex-row">
|
||||
<button class="btn btn-primary me-2 button-fixed-width" type="submit">Сохранить</button>
|
||||
<a class="btn btn-secondary button-fixed-width" th:href="@{/admin/user(page=${page})}">Отмена</a>
|
||||
</div>
|
||||
</form>
|
||||
</main>
|
||||
</body>
|
||||
|
||||
</html>
|
58
build/resources/main/templates/user.html
Normal file
58
build/resources/main/templates/user.html
Normal file
@ -0,0 +1,58 @@
|
||||
<!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" class="w-50 mx-auto">
|
||||
<th:block th:switch="${items.size()}">
|
||||
<h2 th:case="0">Данные отсутствуют</h2>
|
||||
<th:block th:case="*">
|
||||
<h2 class=" text-center">Пользователи</h2>
|
||||
<div>
|
||||
<a th:href="@{/admin/user/edit/(page=${page})}" class="btn btn-primary">Добавить пользователя</a>
|
||||
</div>
|
||||
<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-10"></th>
|
||||
<th scope="col" class="w-10"></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr th:each="user : ${items}">
|
||||
<th scope="row" th:text="${user.id}"></th>
|
||||
<td th:text="${user.login}"></td>
|
||||
<td th:text="${user.email}"></td>
|
||||
<td>
|
||||
<form th:action="@{/admin/user/edit/{id}(id=${user.id})}" method="get">
|
||||
<input type="hidden" th:name="page" th:value="${page}">
|
||||
<button type="submit" class="btn btn-link button-link">Редактировать</button>
|
||||
</form>
|
||||
</td>
|
||||
<td>
|
||||
<form th:action="@{/admin/user/delete/{id}(id=${user.id})}" method="post">
|
||||
<input type="hidden" th:name="page" th:value="${page}">
|
||||
<button type="submit" class="btn btn-link button-link"
|
||||
onclick="return confirm('Вы уверены?')">Удалить</button>
|
||||
</form>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</th:block>
|
||||
<th:block th:replace="~{ pagination :: pagination (
|
||||
url=${'admin/user'},
|
||||
totalPages=${totalPages},
|
||||
currentPage=${currentPage}) }" />
|
||||
</th:block>
|
||||
</main>
|
||||
</body>
|
||||
|
||||
</html>
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
data.mv.db
BIN
data.mv.db
Binary file not shown.
59827
data.trace.db
59827
data.trace.db
File diff suppressed because it is too large
Load Diff
@ -1,18 +1,25 @@
|
||||
package com.example.demo;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import org.springframework.boot.CommandLineRunner;
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
|
||||
import com.example.demo.core.configuration.Constants;
|
||||
import com.example.demo.products.model.ProductEntity;
|
||||
import com.example.demo.products.service.ProductService;
|
||||
import com.example.demo.orders.model.OrderEntity;
|
||||
import com.example.demo.orders.service.OrderService;
|
||||
import com.example.demo.types.model.TypeEntity;
|
||||
import com.example.demo.types.service.TypeService;
|
||||
import com.example.demo.users.model.UserEntity;
|
||||
import com.example.demo.users.model.UserRole;
|
||||
import com.example.demo.users.service.UserService;
|
||||
|
||||
@SpringBootApplication
|
||||
@ -21,11 +28,14 @@ public class DemoApplication implements CommandLineRunner {
|
||||
|
||||
private final TypeService typeService;
|
||||
private final ProductService productService;
|
||||
private final OrderService orderService;
|
||||
private final UserService userService;
|
||||
|
||||
public DemoApplication(TypeService typeService, ProductService productService, UserService userService) {
|
||||
public DemoApplication(TypeService typeService, ProductService productService,
|
||||
OrderService orderService, UserService userService) {
|
||||
this.typeService = typeService;
|
||||
this.productService = productService;
|
||||
this.orderService = orderService;
|
||||
this.userService = userService;
|
||||
}
|
||||
|
||||
@ -36,24 +46,56 @@ public class DemoApplication implements CommandLineRunner {
|
||||
@Override
|
||||
public void run(String... args) throws Exception {
|
||||
if (args.length > 0 && Objects.equals("--populate", args[0])) {
|
||||
log.info("Create default types values");
|
||||
log.info("start");
|
||||
|
||||
log.info("Create default user values");
|
||||
final var admin = new UserEntity("admin", "admin@mail.com", "admin");
|
||||
admin.setRole(UserRole.ADMIN);
|
||||
userService.create(admin);
|
||||
final var user1 = userService.create(new UserEntity("user", "user@gmail.com",
|
||||
Constants.DEFAULT_PASSWORD));
|
||||
|
||||
final var type1 = typeService.create(new TypeEntity("Пицца"));
|
||||
final var type2 = typeService.create(new TypeEntity("Напиток"));
|
||||
final var type3 = typeService.create(new TypeEntity("Закуска"));
|
||||
final var type2 = typeService.create(new TypeEntity("Лимонад"));
|
||||
|
||||
log.info("Create default products values");
|
||||
productService.create(new ProductEntity("Маргарита", type1, 499.00));
|
||||
productService.create(new ProductEntity("Эль Дьябло", type1, 699.00));
|
||||
productService.create(new ProductEntity("Гавайская", type1, 399.00));
|
||||
productService.create(new ProductEntity("Лимонад", type2, 99.00));
|
||||
productService.create(new ProductEntity("Сок", type2, 99.00));
|
||||
productService.create(new ProductEntity("Чай", type2, 49.00));
|
||||
productService.create(new ProductEntity("Картошка фри", type3, 199.00));
|
||||
productService.create(new ProductEntity("Нагетсы", type3, 199.00));
|
||||
final var product1 = productService.create(new ProductEntity(type1, "Цыпленок барбекью", 510.0,
|
||||
"Сочная пицца с кусочками пиццы под вкуснейшим соусом барбекью"));
|
||||
final var product2 = productService.create(new ProductEntity(type2, "Кола", 100.0, "Старая добрая кола"));
|
||||
productService
|
||||
.create(new ProductEntity(type1, "Мексиканская", 500.0,
|
||||
"Обжигающая пицца с БОЛЬШИМ количеством мяса"));
|
||||
productService.create(new ProductEntity(type2, "Фанта", 80.0, "Добрый Фанта"));
|
||||
productService.create(new ProductEntity(type1, "4 сыра", 400.0, "4 нежнейших сыра на хрустящей корочке"));
|
||||
productService.create(new ProductEntity(type2, "Щай", 60.0, "Татарам с молоком)"));
|
||||
productService.create(new ProductEntity(type1, "Пепперени", 400.0, "Классика"));
|
||||
|
||||
log.info("Create default users values");
|
||||
userService.create(new UserEntity("Alex", "Kryukov", "akryu@mail.ru", "password123"));
|
||||
userService.create(new UserEntity("Oleg", "Zyngin", "@mail.ru", "password"));
|
||||
final var product4 = productService
|
||||
.create(new ProductEntity(type1, "Пеппирони", 400.0, "много мяса и сыра"));
|
||||
final var product5 = productService.create(new ProductEntity(type2, "Фанта", 100.0, "Старая добрая кола"));
|
||||
final var product3 = productService
|
||||
.create(new ProductEntity(type1, "Гавайская", 400.0, "много мяса и сыра и ананасик"));
|
||||
final var product6 = productService
|
||||
.create(new ProductEntity(type2, "Спрайт", 100.0, "Освежает, не правда-ли?"));
|
||||
final var product7 = productService
|
||||
.create(new ProductEntity(type1, "Маргарита", 400.0, "много мяса и сыра"));
|
||||
|
||||
final List<ProductEntity> products = new ArrayList<ProductEntity>();
|
||||
products.add(product1);
|
||||
products.add(product2);
|
||||
final List<ProductEntity> products2 = new ArrayList<ProductEntity>();
|
||||
products.add(product4);
|
||||
products.add(product5);
|
||||
|
||||
final List<ProductEntity> products3 = new ArrayList<ProductEntity>();
|
||||
products.add(product3);
|
||||
products.add(product6);
|
||||
products.add(product7);
|
||||
|
||||
log.info("Create default order values");
|
||||
orderService.create(user1.getId(), new OrderEntity(products2));
|
||||
orderService.create(admin.getId(), new OrderEntity(products));
|
||||
orderService.create(user1.getId(), new OrderEntity(products3));
|
||||
orderService.create(admin.getId(), new OrderEntity(products3));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,28 @@
|
||||
package com.example.demo.core.api;
|
||||
|
||||
import org.springframework.web.bind.annotation.ControllerAdvice;
|
||||
import org.springframework.web.bind.annotation.ModelAttribute;
|
||||
|
||||
import com.example.demo.core.session.SessionCart;
|
||||
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import jakarta.servlet.http.HttpSession;
|
||||
|
||||
@ControllerAdvice
|
||||
public class GlobalController {
|
||||
private final SessionCart cart;
|
||||
|
||||
public GlobalController(SessionCart cart) {
|
||||
this.cart = cart;
|
||||
}
|
||||
|
||||
@ModelAttribute("servletPath")
|
||||
String getRequestServletPath(HttpServletRequest request) {
|
||||
return request.getServletPath();
|
||||
}
|
||||
|
||||
@ModelAttribute("totalCart")
|
||||
double getTotalCart(HttpSession session) {
|
||||
return cart.getSum();
|
||||
}
|
||||
}
|
@ -0,0 +1,18 @@
|
||||
package com.example.demo.core.api;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.function.Function;
|
||||
|
||||
import org.springframework.data.domain.Page;
|
||||
|
||||
public class PageAttributesMapper {
|
||||
private PageAttributesMapper() {
|
||||
}
|
||||
|
||||
public static <E, D> Map<String, Object> toAttributes(Page<E> page, Function<E, D> mapper) {
|
||||
return Map.of(
|
||||
"items", page.getContent().stream().map(mapper::apply).toList(),
|
||||
"currentPage", page.getNumber(),
|
||||
"totalPages", page.getTotalPages());
|
||||
}
|
||||
}
|
@ -3,7 +3,16 @@ package com.example.demo.core.configuration;
|
||||
public class Constants {
|
||||
public static final String SEQUENCE_NAME = "hibernate_sequence";
|
||||
|
||||
public static final String API_URL = "/api/1.0";
|
||||
public static final int DEFAULT_PAGE_SIZE = 5;
|
||||
|
||||
public static final String REDIRECT_VIEW = "redirect:";
|
||||
|
||||
public static final String ADMIN_PREFIX = "/admin";
|
||||
|
||||
public static final String LOGIN_URL = "/login";
|
||||
public static final String LOGOUT_URL = "/logout";
|
||||
|
||||
public static final String DEFAULT_PASSWORD = "123456";
|
||||
|
||||
private Constants() {
|
||||
}
|
||||
|
@ -1,13 +1,23 @@
|
||||
package com.example.demo.core.configuration;
|
||||
|
||||
import org.modelmapper.ModelMapper;
|
||||
import org.modelmapper.PropertyMap;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
import com.example.demo.core.model.BaseEntity;
|
||||
|
||||
@Configuration
|
||||
public class MapperConfiguration {
|
||||
@Bean
|
||||
ModelMapper modelMapper() {
|
||||
return new ModelMapper();
|
||||
final ModelMapper mapper = new ModelMapper();
|
||||
mapper.addMappings(new PropertyMap<Object, BaseEntity>() {
|
||||
@Override
|
||||
protected void configure() {
|
||||
skip(destination.getId());
|
||||
}
|
||||
});
|
||||
return mapper;
|
||||
}
|
||||
}
|
||||
|
@ -1,15 +1,13 @@
|
||||
package com.example.demo.core.configuration;
|
||||
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.lang.NonNull;
|
||||
import org.springframework.web.servlet.config.annotation.CorsRegistry;
|
||||
import org.springframework.web.servlet.config.annotation.ViewControllerRegistry;
|
||||
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
|
||||
|
||||
@Configuration
|
||||
public class WebConfiguration implements WebMvcConfigurer {
|
||||
@Override
|
||||
public void addCorsMappings(@NonNull CorsRegistry registry) {
|
||||
registry.addMapping("/**")
|
||||
.allowedMethods("GET", "POST", "PUT", "DELETE");
|
||||
public void addViewControllers(ViewControllerRegistry registry) {
|
||||
registry.addViewController("/login").setViewName("login");
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,53 @@
|
||||
package com.example.demo.core.error;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.core.annotation.AnnotationUtils;
|
||||
import org.springframework.web.bind.annotation.ControllerAdvice;
|
||||
import org.springframework.web.bind.annotation.ExceptionHandler;
|
||||
import org.springframework.web.bind.annotation.ResponseStatus;
|
||||
import org.springframework.web.servlet.ModelAndView;
|
||||
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
|
||||
@ControllerAdvice
|
||||
public class AdviceController {
|
||||
private final Logger log = LoggerFactory.getLogger(AdviceController.class);
|
||||
|
||||
private static Throwable getRootCause(Throwable throwable) {
|
||||
Throwable rootCause = throwable;
|
||||
while (rootCause.getCause() != null && rootCause.getCause() != rootCause) {
|
||||
rootCause = rootCause.getCause();
|
||||
}
|
||||
return rootCause;
|
||||
}
|
||||
|
||||
private static Map<String, Object> getAttributes(HttpServletRequest request, Throwable throwable) {
|
||||
final Throwable rootCause = getRootCause(throwable);
|
||||
final StackTraceElement firstError = rootCause.getStackTrace()[0];
|
||||
return Map.of(
|
||||
"message", rootCause.getMessage(),
|
||||
"url", request.getRequestURL(),
|
||||
"exception", rootCause.getClass().getName(),
|
||||
"file", firstError.getFileName(),
|
||||
"method", firstError.getMethodName(),
|
||||
"line", firstError.getLineNumber());
|
||||
}
|
||||
|
||||
@ExceptionHandler(value = Exception.class)
|
||||
public ModelAndView defaultErrorHandler(HttpServletRequest request, Throwable throwable) throws Throwable {
|
||||
if (AnnotationUtils.findAnnotation(throwable.getClass(),
|
||||
ResponseStatus.class) != null) {
|
||||
throw throwable;
|
||||
}
|
||||
|
||||
log.error("{}", throwable.getMessage());
|
||||
throwable.printStackTrace();
|
||||
final ModelAndView model = new ModelAndView();
|
||||
model.addAllObjects(getAttributes(request, throwable));
|
||||
model.setViewName("error");
|
||||
return model;
|
||||
}
|
||||
}
|
@ -3,6 +3,7 @@ package com.example.demo.core.model;
|
||||
import com.example.demo.core.configuration.Constants;
|
||||
|
||||
import jakarta.persistence.GeneratedValue;
|
||||
import jakarta.persistence.GenerationType;
|
||||
import jakarta.persistence.Id;
|
||||
import jakarta.persistence.MappedSuperclass;
|
||||
import jakarta.persistence.SequenceGenerator;
|
||||
@ -10,7 +11,7 @@ import jakarta.persistence.SequenceGenerator;
|
||||
@MappedSuperclass
|
||||
public abstract class BaseEntity {
|
||||
@Id
|
||||
@GeneratedValue()
|
||||
@GeneratedValue(strategy = GenerationType.SEQUENCE, generator = Constants.SEQUENCE_NAME)
|
||||
@SequenceGenerator(name = Constants.SEQUENCE_NAME, sequenceName = Constants.SEQUENCE_NAME, allocationSize = 1)
|
||||
protected Long id;
|
||||
|
||||
|
@ -0,0 +1,63 @@
|
||||
package com.example.demo.core.security;
|
||||
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.security.authentication.dao.DaoAuthenticationProvider;
|
||||
import org.springframework.security.config.Customizer;
|
||||
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
|
||||
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
|
||||
import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer;
|
||||
import org.springframework.security.config.annotation.web.configurers.HeadersConfigurer.FrameOptionsConfig;
|
||||
import org.springframework.security.core.userdetails.UserDetailsService;
|
||||
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
|
||||
import org.springframework.security.crypto.password.PasswordEncoder;
|
||||
import org.springframework.security.web.SecurityFilterChain;
|
||||
|
||||
import com.example.demo.core.configuration.Constants;
|
||||
import com.example.demo.users.api.UserSignupController;
|
||||
import com.example.demo.users.model.UserRole;
|
||||
|
||||
@Configuration
|
||||
@EnableWebSecurity
|
||||
public class SecurityConfiguration {
|
||||
@Bean
|
||||
SecurityFilterChain filterChain(HttpSecurity httpSecurity) throws Exception {
|
||||
httpSecurity.headers(headers -> headers.frameOptions(FrameOptionsConfig::sameOrigin));
|
||||
httpSecurity.csrf(AbstractHttpConfigurer::disable);
|
||||
httpSecurity.cors(Customizer.withDefaults());
|
||||
|
||||
httpSecurity.authorizeHttpRequests(requests -> requests
|
||||
.requestMatchers("/css/**", "/webjars/**", "/*.svg")
|
||||
.permitAll());
|
||||
|
||||
httpSecurity.authorizeHttpRequests(requests -> requests
|
||||
.requestMatchers(Constants.ADMIN_PREFIX + "/**").hasRole(UserRole.ADMIN.name())
|
||||
.requestMatchers("/h2-console/**").hasRole(UserRole.ADMIN.name())
|
||||
.requestMatchers(UserSignupController.URL).anonymous()
|
||||
.requestMatchers(Constants.LOGIN_URL).anonymous()
|
||||
.anyRequest().authenticated());
|
||||
|
||||
httpSecurity.formLogin(formLogin -> formLogin
|
||||
.loginPage(Constants.LOGIN_URL));
|
||||
|
||||
httpSecurity.rememberMe(rememberMe -> rememberMe.key("uniqueAndSecret"));
|
||||
|
||||
httpSecurity.logout(logout -> logout
|
||||
.deleteCookies("JSESSIONID"));
|
||||
|
||||
return httpSecurity.build();
|
||||
}
|
||||
|
||||
@Bean
|
||||
DaoAuthenticationProvider authenticationProvider(UserDetailsService userDetailsService) {
|
||||
final DaoAuthenticationProvider authProvider = new DaoAuthenticationProvider();
|
||||
authProvider.setUserDetailsService(userDetailsService);
|
||||
authProvider.setPasswordEncoder(passwordEncoder());
|
||||
return authProvider;
|
||||
}
|
||||
|
||||
@Bean
|
||||
PasswordEncoder passwordEncoder() {
|
||||
return new BCryptPasswordEncoder();
|
||||
}
|
||||
}
|
@ -0,0 +1,64 @@
|
||||
package com.example.demo.core.security;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Set;
|
||||
|
||||
import org.springframework.security.core.GrantedAuthority;
|
||||
import org.springframework.security.core.userdetails.UserDetails;
|
||||
|
||||
import com.example.demo.users.model.UserEntity;
|
||||
|
||||
public class UserPrincipal implements UserDetails{
|
||||
private final long id;
|
||||
private final String username;
|
||||
private final String password;
|
||||
private final Set<? extends GrantedAuthority> roles;
|
||||
private final boolean active;
|
||||
|
||||
public UserPrincipal(UserEntity user) {
|
||||
this.id = user.getId();
|
||||
this.username = user.getLogin();
|
||||
this.password = user.getPassword();
|
||||
this.roles = Set.of(user.getRole());
|
||||
this.active = true;
|
||||
}
|
||||
|
||||
public Long getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getUsername() {
|
||||
return username;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getPassword() {
|
||||
return password;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<? extends GrantedAuthority> getAuthorities() {
|
||||
return roles;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEnabled() {
|
||||
return active;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isAccountNonExpired() {
|
||||
return isEnabled();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isAccountNonLocked() {
|
||||
return isEnabled();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isCredentialsNonExpired() {
|
||||
return isEnabled();
|
||||
}
|
||||
}
|
14
src/main/java/com/example/demo/core/session/SessionCart.java
Normal file
14
src/main/java/com/example/demo/core/session/SessionCart.java
Normal file
@ -0,0 +1,14 @@
|
||||
package com.example.demo.core.session;
|
||||
|
||||
import java.util.HashMap;
|
||||
|
||||
import com.example.demo.users.api.UserCartDto;
|
||||
|
||||
public class SessionCart extends HashMap<Integer, UserCartDto> {
|
||||
public double getSum() {
|
||||
return this.values().stream()
|
||||
.map(item -> item.getPrice())
|
||||
.mapToDouble(Double::doubleValue)
|
||||
.sum();
|
||||
}
|
||||
}
|
@ -0,0 +1,16 @@
|
||||
package com.example.demo.core.session;
|
||||
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.Scope;
|
||||
import org.springframework.context.annotation.ScopedProxyMode;
|
||||
import org.springframework.web.context.WebApplicationContext;
|
||||
|
||||
@Configuration
|
||||
public class SessionHelper {
|
||||
@Bean
|
||||
@Scope(value = WebApplicationContext.SCOPE_SESSION, proxyMode = ScopedProxyMode.TARGET_CLASS)
|
||||
SessionCart todos() {
|
||||
return new SessionCart();
|
||||
}
|
||||
}
|
@ -1,103 +0,0 @@
|
||||
package com.example.demo.orders.api;
|
||||
|
||||
import java.sql.Date;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.modelmapper.ModelMapper;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
import com.example.demo.core.configuration.Constants;
|
||||
import com.example.demo.order_lines.api.OrderLineDto;
|
||||
import com.example.demo.order_lines.model.OrderLineEntity;
|
||||
import com.example.demo.orders.model.OrderEntity;
|
||||
import com.example.demo.orders.service.OrderService;
|
||||
import com.example.demo.products.model.ProductEntity;
|
||||
import com.example.demo.products.service.ProductService;
|
||||
import com.example.demo.users.model.UserEntity;
|
||||
import com.example.demo.users.service.UserService;
|
||||
import jakarta.validation.Valid;
|
||||
|
||||
@RestController
|
||||
@RequestMapping(Constants.API_URL + "/order")
|
||||
public class OrderController {
|
||||
private final OrderService orderService;
|
||||
private final UserService userService;
|
||||
private final ModelMapper modelMapper;
|
||||
private final ProductService productService;
|
||||
|
||||
public OrderController(OrderService orderService, UserService userService, ModelMapper modelMapper,
|
||||
ProductService productService) {
|
||||
this.orderService = orderService;
|
||||
this.userService = userService;
|
||||
this.productService = productService;
|
||||
this.modelMapper = modelMapper;
|
||||
}
|
||||
|
||||
private OrderDto toDto(OrderEntity entity) {
|
||||
var dto = new OrderDto();
|
||||
dto.setId(entity.getId());
|
||||
dto.setUserId(entity.getUser().getId());
|
||||
dto.setDate(new Date(System.currentTimeMillis()));
|
||||
for (OrderLineEntity orderLineEntity : entity.getLines()) {
|
||||
OrderLineDto orderLineDto = new OrderLineDto();
|
||||
orderLineDto.setProductId(orderLineEntity.getProduct().getId());
|
||||
orderLineDto.setCount(orderLineEntity.getCount());
|
||||
// Вычисляем общую цену строки заказа
|
||||
Double totalPriceLine = orderLineEntity.getProduct().getPrice() * orderLineEntity.getCount();
|
||||
orderLineDto.setTotalPriceLine(totalPriceLine);
|
||||
dto.addOrderLine(orderLineDto);
|
||||
}
|
||||
return dto;
|
||||
}
|
||||
|
||||
public OrderEntity toEntity(OrderDto dto) {
|
||||
OrderEntity entity = modelMapper.map(dto, OrderEntity.class);
|
||||
UserEntity user = userService.get(dto.getUserId());
|
||||
entity.setUser(user);
|
||||
|
||||
List<OrderLineEntity> orderLines = dto.getLines().stream()
|
||||
.map(lineDto -> {
|
||||
|
||||
OrderLineEntity orderLineEntity = modelMapper.map(lineDto, OrderLineEntity.class);
|
||||
orderLineEntity.setId(null);
|
||||
|
||||
// Получаем продукт по id
|
||||
ProductEntity product = productService.get(lineDto.getProductId());
|
||||
|
||||
orderLineEntity.setProduct(product);
|
||||
return orderLineEntity;
|
||||
})
|
||||
.collect(Collectors.toList());
|
||||
|
||||
orderLines.stream().forEach(entity::addOrderLine);
|
||||
|
||||
return entity;
|
||||
}
|
||||
|
||||
@GetMapping
|
||||
public List<OrderDto> getAll(@RequestParam(name = "userId", defaultValue = "0") Long userId,
|
||||
@RequestParam(name = "lines", defaultValue = "") List<Long> lines) {
|
||||
return orderService.getAll(userId, lines).stream().map(this::toDto).toList();
|
||||
}
|
||||
|
||||
@GetMapping("/{id}")
|
||||
public OrderDto get(@PathVariable(name = "id") Long id) {
|
||||
return toDto(orderService.get(id));
|
||||
}
|
||||
|
||||
@PostMapping
|
||||
public OrderDto create(@RequestBody @Valid OrderDto dto) {
|
||||
OrderEntity orderEntity = toEntity(dto);
|
||||
return toDto(orderService.create(orderEntity));
|
||||
}
|
||||
|
||||
@PutMapping("/{id}")
|
||||
public OrderDto update(@PathVariable(name = "id") Long id, @RequestBody OrderDto dto) {
|
||||
return toDto(orderService.update(id, toEntity(dto)));
|
||||
}
|
||||
|
||||
@DeleteMapping("/{id}")
|
||||
public OrderDto delete(@PathVariable(name = "id") Long id) {
|
||||
return toDto(orderService.delete(id));
|
||||
}
|
||||
}
|
@ -1,24 +1,14 @@
|
||||
package com.example.demo.orders.api;
|
||||
|
||||
import com.example.demo.order_lines.api.OrderLineDto;
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
|
||||
import jakarta.validation.constraints.Min;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import java.sql.Date;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
|
||||
public class OrderDto {
|
||||
@JsonProperty(access = JsonProperty.Access.READ_ONLY)
|
||||
private Long id;
|
||||
@NotNull
|
||||
@Min(1)
|
||||
private Long userId;
|
||||
@JsonProperty(access = JsonProperty.Access.READ_ONLY)
|
||||
private Date date = getDate();
|
||||
|
||||
private final List<OrderLineDto> lines = new ArrayList<>();
|
||||
private final List<Long> products = new ArrayList<>();
|
||||
|
||||
public Long getId() {
|
||||
return id;
|
||||
@ -28,30 +18,12 @@ public class OrderDto {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public Long getUserId() {
|
||||
return userId;
|
||||
public List<Long> getProducts() {
|
||||
return products;
|
||||
}
|
||||
|
||||
public void setUserId(Long userId) {
|
||||
this.userId = userId;
|
||||
public void setProducts(List<Long> products) {
|
||||
this.products.clear();
|
||||
this.products.addAll(products);
|
||||
}
|
||||
|
||||
public void setDate(Date date) {
|
||||
this.date = date;
|
||||
}
|
||||
|
||||
public List<OrderLineDto> getLines() {
|
||||
return lines;
|
||||
}
|
||||
|
||||
// метод для добавления строк заказа
|
||||
public void addOrderLine(OrderLineDto orderLine) {
|
||||
this.lines.add(orderLine);
|
||||
}
|
||||
|
||||
@JsonProperty(access = JsonProperty.Access.READ_ONLY)
|
||||
public Date getDate() {
|
||||
return date;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -1,37 +1,36 @@
|
||||
package com.example.demo.orders.model;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
|
||||
import com.example.demo.core.model.BaseEntity;
|
||||
import com.example.demo.order_lines.model.OrderLineEntity;
|
||||
import com.example.demo.products.model.ProductEntity;
|
||||
import com.example.demo.users.model.UserEntity;
|
||||
|
||||
import jakarta.persistence.CascadeType;
|
||||
import jakarta.persistence.Entity;
|
||||
import jakarta.persistence.FetchType;
|
||||
import jakarta.persistence.JoinColumn;
|
||||
import jakarta.persistence.ManyToMany;
|
||||
import jakarta.persistence.ManyToOne;
|
||||
import jakarta.persistence.Table;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
@Entity
|
||||
@Table(name = "orders")
|
||||
public class OrderEntity extends BaseEntity {
|
||||
@ManyToOne
|
||||
@JoinColumn(name = "userId", nullable = false)
|
||||
private UserEntity user;
|
||||
@ManyToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER)
|
||||
private final List<OrderLineEntity> lines = new ArrayList<>();
|
||||
private Double totalPrice; // Поле для общей стоимости заказа
|
||||
@ManyToMany()
|
||||
private Set<ProductEntity> products = new HashSet<>();
|
||||
|
||||
public OrderEntity() {
|
||||
super();
|
||||
}
|
||||
|
||||
public OrderEntity(UserEntity user) {
|
||||
this.user = user;
|
||||
public OrderEntity(List<ProductEntity> products) {
|
||||
this.products.clear();
|
||||
this.products.addAll(products);
|
||||
}
|
||||
|
||||
public UserEntity getUser() {
|
||||
@ -40,49 +39,33 @@ public class OrderEntity extends BaseEntity {
|
||||
|
||||
public void setUser(UserEntity user) {
|
||||
this.user = user;
|
||||
}
|
||||
|
||||
public List<OrderLineEntity> getLines() {
|
||||
return lines;
|
||||
}
|
||||
|
||||
public Double getTotalPrice() {
|
||||
return totalPrice;
|
||||
}
|
||||
|
||||
public void addOrderLine(OrderLineEntity orderLine) {
|
||||
this.lines.add(orderLine);
|
||||
recalculateTotalPrice();
|
||||
}
|
||||
|
||||
public void removeOrderLine(OrderLineEntity orderLine) {
|
||||
this.lines.remove(orderLine);
|
||||
recalculateTotalPrice();
|
||||
}
|
||||
|
||||
private void recalculateTotalPrice() {
|
||||
totalPrice = 0.0;
|
||||
for (OrderLineEntity line : lines) {
|
||||
totalPrice += line.getTotalPrice();
|
||||
if (!user.getOrders().contains(this)) {
|
||||
user.getOrders().add(this);
|
||||
}
|
||||
}
|
||||
|
||||
public Set<ProductEntity> getProducts() {
|
||||
return products;
|
||||
}
|
||||
|
||||
public void setProducts(ProductEntity product) {
|
||||
this.products.add(product);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(id, user, lines, totalPrice);
|
||||
return Objects.hash(id, products);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unlikely-arg-user")
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (this == obj)
|
||||
return true;
|
||||
if (obj == null || getClass() != obj.getClass())
|
||||
return false;
|
||||
|
||||
final OrderEntity other = (OrderEntity) obj;
|
||||
return Objects.equals(other.getId(), id)
|
||||
&& Objects.equals(other.getUser(), user)
|
||||
&& Objects.equals(other.getLines(), lines)
|
||||
&& Objects.equals(other.getTotalPrice(), totalPrice);
|
||||
&& Objects.equals(other.getProducts(), products);
|
||||
}
|
||||
}
|
||||
|
@ -1,19 +1,26 @@
|
||||
package com.example.demo.orders.repository;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
import org.springframework.data.domain.Page;
|
||||
import org.springframework.data.domain.Pageable;
|
||||
import org.springframework.data.jpa.repository.Query;
|
||||
import org.springframework.data.repository.CrudRepository;
|
||||
import org.springframework.data.repository.PagingAndSortingRepository;
|
||||
|
||||
import com.example.demo.orders.model.OrderEntity;
|
||||
|
||||
public interface OrderRepository extends CrudRepository<OrderEntity, Long> {
|
||||
@SuppressWarnings("null")
|
||||
List<OrderEntity> findAll();
|
||||
|
||||
List<OrderEntity> findByUserIdAndLinesProductId(long userId, List<Long> lines);
|
||||
|
||||
List<OrderEntity> findById(long id);
|
||||
public interface OrderRepository
|
||||
extends CrudRepository<OrderEntity, Long>, PagingAndSortingRepository<OrderEntity, Long> {
|
||||
Optional<OrderEntity> findOneByUserIdAndId(long userId, long id);
|
||||
|
||||
@Query("select o from OrderEntity o join fetch o.products where o.user.id = ?1")
|
||||
List<OrderEntity> findByUserId(long userId);
|
||||
|
||||
@Query("select o from OrderEntity o join fetch o.products where o.user.id = ?1")
|
||||
Page<OrderEntity> findByUserId(long userId, Pageable pageable);
|
||||
|
||||
List<OrderEntity> findAll();
|
||||
|
||||
}
|
||||
|
@ -1,69 +1,78 @@
|
||||
package com.example.demo.orders.service;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
import org.springframework.data.domain.Page;
|
||||
import org.springframework.data.domain.PageRequest;
|
||||
import org.springframework.data.domain.Pageable;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.data.domain.Sort;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import com.example.demo.core.error.NotFoundException;
|
||||
import com.example.demo.order_lines.model.OrderLineEntity;
|
||||
import com.example.demo.orders.model.OrderEntity;
|
||||
import com.example.demo.orders.repository.OrderRepository;
|
||||
import com.example.demo.users.model.UserEntity;
|
||||
import com.example.demo.users.service.UserService;
|
||||
|
||||
@Service
|
||||
public class OrderService {
|
||||
private final OrderRepository repository;
|
||||
private final UserService userService;
|
||||
|
||||
public OrderService(OrderRepository repository) {
|
||||
public OrderService(OrderRepository repository, UserService userService) {
|
||||
this.repository = repository;
|
||||
this.userService = userService;
|
||||
}
|
||||
|
||||
@Transactional(readOnly = true)
|
||||
public List<OrderEntity> getAll(Long userId, List<Long> lines) {
|
||||
if (userId != 0L && !lines.isEmpty()) {
|
||||
return repository.findByUserIdAndLinesProductId(userId, lines);
|
||||
}
|
||||
if (userId == 0L && !lines.isEmpty()) {
|
||||
return repository.findAll();
|
||||
}
|
||||
if (userId != 0L && lines.isEmpty()) {
|
||||
return repository.findByUserId(userId);
|
||||
}
|
||||
public Page<OrderEntity> getAll(long userId, int page, int size) {
|
||||
final Pageable pageRequest = PageRequest.of(page, size, Sort.by("id"));
|
||||
userService.get(userId);
|
||||
return repository.findByUserId(userId, pageRequest);
|
||||
}
|
||||
|
||||
@Transactional(readOnly = true)
|
||||
public List<OrderEntity> getAll(long userId) {
|
||||
userService.get(userId);
|
||||
return repository.findByUserId(userId);
|
||||
}
|
||||
|
||||
public List<OrderEntity> getAll() {
|
||||
return repository.findAll();
|
||||
}
|
||||
|
||||
@Transactional(readOnly = true)
|
||||
public OrderEntity get(Long id) {
|
||||
// Используем findById репозитория для извлечения OrderEntity по id
|
||||
Optional<OrderEntity> optionalOrderEntity = repository.findById(id);
|
||||
|
||||
// Используем orElseThrow для выброса исключения, если OrderEntity не найден
|
||||
OrderEntity orderEntity = optionalOrderEntity.orElseThrow(() -> new NotFoundException(OrderEntity.class, id));
|
||||
return orderEntity;
|
||||
public OrderEntity get(long userId, long id) {
|
||||
userService.get(userId);
|
||||
return repository.findOneByUserIdAndId(userId, id)
|
||||
.orElseThrow(() -> new NotFoundException(OrderEntity.class, id));
|
||||
}
|
||||
|
||||
public OrderEntity create(OrderEntity entity) {
|
||||
@Transactional
|
||||
public OrderEntity create(long userId, OrderEntity entity) {
|
||||
if (entity == null) {
|
||||
throw new IllegalArgumentException("Entity is null");
|
||||
}
|
||||
final UserEntity existsUser = userService.get(userId);
|
||||
entity.setUser(existsUser);
|
||||
return repository.save(entity);
|
||||
}
|
||||
|
||||
@Transactional
|
||||
public OrderEntity update(Long id, OrderEntity entity) {
|
||||
final OrderEntity existEntity = get(id);
|
||||
existEntity.setUser(entity.getUser());
|
||||
|
||||
// Очищаем текущие строки заказа и добавляем новые
|
||||
existEntity.getLines().clear();
|
||||
for (OrderLineEntity newOrderLine : entity.getLines()) {
|
||||
existEntity.addOrderLine(newOrderLine);
|
||||
public OrderEntity createAll(long userId, OrderEntity entitiy) {
|
||||
if (entitiy == null) {
|
||||
throw new IllegalArgumentException("Orders list is null or empty");
|
||||
}
|
||||
|
||||
return repository.save(existEntity);
|
||||
final UserEntity existsUser = userService.get(userId);
|
||||
entitiy.setUser(existsUser);
|
||||
return repository.save(entitiy);
|
||||
}
|
||||
|
||||
@Transactional
|
||||
public OrderEntity delete(Long id) {
|
||||
final OrderEntity existsEntity = get(id);
|
||||
public OrderEntity delete(long userId, long id) {
|
||||
userService.get(userId);
|
||||
final OrderEntity existsEntity = get(userId, id);
|
||||
repository.delete(existsEntity);
|
||||
return existsEntity;
|
||||
}
|
||||
|
@ -1,43 +1,65 @@
|
||||
package com.example.demo.products.api;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.modelmapper.ModelMapper;
|
||||
import org.springframework.web.bind.annotation.DeleteMapping;
|
||||
import org.springframework.stereotype.Controller;
|
||||
import org.springframework.ui.Model;
|
||||
import org.springframework.validation.BindingResult;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.ModelAttribute;
|
||||
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.RequestParam;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
import org.springframework.web.servlet.mvc.support.RedirectAttributes;
|
||||
|
||||
import com.example.demo.core.api.PageAttributesMapper;
|
||||
import com.example.demo.core.configuration.Constants;
|
||||
import com.example.demo.products.model.ProductEntity;
|
||||
import com.example.demo.products.service.ProductService;
|
||||
import com.example.demo.types.api.TypeDto;
|
||||
import com.example.demo.types.model.TypeEntity;
|
||||
import com.example.demo.types.service.TypeService;
|
||||
|
||||
import jakarta.validation.Valid;
|
||||
|
||||
@RestController
|
||||
@RequestMapping(Constants.API_URL + "/product")
|
||||
@Controller
|
||||
@RequestMapping(ProductController.URL)
|
||||
public class ProductController {
|
||||
|
||||
public static final String URL = Constants.ADMIN_PREFIX + "/product";
|
||||
private static final String PRODUCT_VIEW = "product";
|
||||
private static final String TOP_PRODUCTS_VIEW = "top-products";
|
||||
private static final String PRODUCT_EDIT_VIEW = "product-edit";
|
||||
private static final String PAGE_ATTRIBUTE = "page";
|
||||
private static final String PRODUCT_ATTRIBUTE = "product";
|
||||
|
||||
private final ProductService productService;
|
||||
private final TypeService typeService;
|
||||
private final ModelMapper modelMapper;
|
||||
|
||||
public ProductController(ProductService productService, TypeService typeService, ModelMapper modelMapper) {
|
||||
this.productService = productService;
|
||||
this.typeService = typeService;
|
||||
this.modelMapper = modelMapper;
|
||||
this.typeService = typeService;
|
||||
}
|
||||
|
||||
private ProductDto toDto(ProductEntity entity) {
|
||||
ProductDto dto = modelMapper.map(entity, ProductDto.class);
|
||||
var dto = new ProductDto();
|
||||
dto.setId(entity.getId());
|
||||
dto.setDescription(entity.getDescription());
|
||||
dto.setName(entity.getName());
|
||||
dto.setPrice(entity.getPrice());
|
||||
dto.setTypeId(entity.getType().getId());
|
||||
return dto;
|
||||
}
|
||||
|
||||
private TypeDto toTypeDto(TypeEntity entity) {
|
||||
return modelMapper.map(entity, TypeDto.class);
|
||||
}
|
||||
|
||||
private ProductEntity toEntity(ProductDto dto) {
|
||||
final ProductEntity entity = modelMapper.map(dto, ProductEntity.class);
|
||||
entity.setType(typeService.get(dto.getTypeId()));
|
||||
@ -45,27 +67,90 @@ public class ProductController {
|
||||
}
|
||||
|
||||
@GetMapping
|
||||
public List<ProductDto> getAll(@RequestParam(name = "typeId", defaultValue = "0") Long typeId) {
|
||||
return productService.getAll(typeId).stream().map(this::toDto).toList();
|
||||
public String getAll(
|
||||
@RequestParam(name = PAGE_ATTRIBUTE, defaultValue = "0") int page,
|
||||
Model model) {
|
||||
final Map<String, Object> attributes = PageAttributesMapper.toAttributes(
|
||||
productService.getAll(0, page, Constants.DEFAULT_PAGE_SIZE), this::toDto);
|
||||
model.addAllAttributes(attributes);
|
||||
model.addAttribute(PAGE_ATTRIBUTE, page);
|
||||
return PRODUCT_VIEW;
|
||||
}
|
||||
|
||||
@GetMapping("/{id}")
|
||||
public ProductDto get(@PathVariable(name = "id") Long id) {
|
||||
return toDto(productService.get(id));
|
||||
@GetMapping("/edit/")
|
||||
public String create(
|
||||
@RequestParam(name = PAGE_ATTRIBUTE, defaultValue = "0") int page,
|
||||
Model model) {
|
||||
model.addAttribute(PRODUCT_ATTRIBUTE, new ProductDto());
|
||||
model.addAttribute("types", typeService.getAll().stream().map(this::toTypeDto).toList());
|
||||
model.addAttribute(PAGE_ATTRIBUTE, page);
|
||||
return PRODUCT_EDIT_VIEW;
|
||||
}
|
||||
|
||||
@PostMapping
|
||||
public ProductDto create(@RequestBody @Valid ProductDto dto) {
|
||||
return toDto(productService.create(toEntity(dto)));
|
||||
@PostMapping("/edit/")
|
||||
public String create(
|
||||
@RequestParam(name = PAGE_ATTRIBUTE, defaultValue = "0") int page,
|
||||
@ModelAttribute(name = PRODUCT_ATTRIBUTE) @Valid ProductDto product,
|
||||
BindingResult bindingResult,
|
||||
Model model,
|
||||
RedirectAttributes redirectAttributes) {
|
||||
if (bindingResult.hasErrors()) {
|
||||
model.addAttribute(PAGE_ATTRIBUTE, page);
|
||||
return PRODUCT_EDIT_VIEW;
|
||||
}
|
||||
redirectAttributes.addAttribute(PAGE_ATTRIBUTE, page);
|
||||
productService.create(toEntity(product));
|
||||
return Constants.REDIRECT_VIEW + URL;
|
||||
}
|
||||
|
||||
@PutMapping("/{id}")
|
||||
public ProductDto update(@PathVariable(name = "id") Long id, @RequestBody ProductDto dto) {
|
||||
return toDto(productService.update(id, toEntity(dto)));
|
||||
@GetMapping("/edit/{id}")
|
||||
public String update(
|
||||
@PathVariable(name = "id") Long id,
|
||||
@RequestParam(name = PAGE_ATTRIBUTE, defaultValue = "0") int page,
|
||||
Model model) {
|
||||
if (id <= 0) {
|
||||
throw new IllegalArgumentException();
|
||||
}
|
||||
model.addAttribute(PRODUCT_ATTRIBUTE, toDto(productService.get(id)));
|
||||
model.addAttribute(PAGE_ATTRIBUTE, page);
|
||||
model.addAttribute("types", typeService.getAll().stream().map(this::toTypeDto).toList());
|
||||
return PRODUCT_EDIT_VIEW;
|
||||
}
|
||||
|
||||
@DeleteMapping("/{id}")
|
||||
public ProductDto delete(@PathVariable(name = "id") Long id) {
|
||||
return toDto(productService.delete(id));
|
||||
@PostMapping("/edit/{id}")
|
||||
public String update(
|
||||
@PathVariable(name = "id") Long id,
|
||||
@RequestParam(name = PAGE_ATTRIBUTE, defaultValue = "0") int page,
|
||||
@ModelAttribute(name = PRODUCT_ATTRIBUTE) @Valid ProductDto product,
|
||||
BindingResult bindingResult,
|
||||
Model model,
|
||||
RedirectAttributes redirectAttributes) {
|
||||
if (bindingResult.hasErrors()) {
|
||||
model.addAttribute(PAGE_ATTRIBUTE, page);
|
||||
return PRODUCT_EDIT_VIEW;
|
||||
}
|
||||
if (id <= 0) {
|
||||
throw new IllegalArgumentException();
|
||||
}
|
||||
redirectAttributes.addAttribute(PAGE_ATTRIBUTE, page);
|
||||
productService.update(id, toEntity(product));
|
||||
return Constants.REDIRECT_VIEW + URL;
|
||||
}
|
||||
|
||||
@PostMapping("/delete/{id}")
|
||||
public String delete(
|
||||
@PathVariable(name = "id") Long id,
|
||||
@RequestParam(name = PAGE_ATTRIBUTE, defaultValue = "0") int page,
|
||||
RedirectAttributes redirectAttributes) {
|
||||
redirectAttributes.addAttribute(PAGE_ATTRIBUTE, page);
|
||||
productService.delete(id);
|
||||
return Constants.REDIRECT_VIEW + URL;
|
||||
}
|
||||
|
||||
@GetMapping("/top")
|
||||
public String getTopProducts(Model model) {
|
||||
List<ProductEntity> topProducts = productService.getTopFiveMostSoldProducts();
|
||||
model.addAttribute("topProducts", topProducts.stream().map(this::toDto).toList());
|
||||
return TOP_PRODUCTS_VIEW;
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +1,7 @@
|
||||
package com.example.demo.products.api;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import jakarta.validation.constraints.Min;
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
@ -11,13 +12,16 @@ public class ProductDto {
|
||||
@NotNull
|
||||
@Min(1)
|
||||
private Long typeId;
|
||||
@NotNull
|
||||
private final List<Long> genres = new ArrayList<>();
|
||||
@NotNull
|
||||
@Min(100)
|
||||
private Double price;
|
||||
@NotBlank
|
||||
private String name;
|
||||
@NotNull
|
||||
@Min(1)
|
||||
private Double price;
|
||||
@NotBlank
|
||||
private String description;
|
||||
|
||||
@JsonProperty(access = JsonProperty.Access.READ_ONLY)
|
||||
public Long getId() {
|
||||
return id;
|
||||
}
|
||||
@ -34,12 +38,13 @@ public class ProductDto {
|
||||
this.typeId = typeId;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
public List<Long> getGenres() {
|
||||
return genres;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
public void setGenres(List<Long> genres) {
|
||||
this.genres.clear();
|
||||
this.genres.addAll(genres);
|
||||
}
|
||||
|
||||
public Double getPrice() {
|
||||
@ -50,8 +55,20 @@ public class ProductDto {
|
||||
this.price = price;
|
||||
}
|
||||
|
||||
@JsonProperty(access = JsonProperty.Access.READ_ONLY)
|
||||
public Double getSum() {
|
||||
return price;
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public String getDescription() {
|
||||
return description;
|
||||
}
|
||||
|
||||
public void setDescription(String description) {
|
||||
this.description = description;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -1,5 +1,7 @@
|
||||
package com.example.demo.products.model;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
import com.example.demo.core.model.BaseEntity;
|
||||
@ -8,29 +10,31 @@ import com.example.demo.types.model.TypeEntity;
|
||||
import jakarta.persistence.Column;
|
||||
import jakarta.persistence.Entity;
|
||||
import jakarta.persistence.JoinColumn;
|
||||
import jakarta.persistence.ManyToMany;
|
||||
import jakarta.persistence.ManyToOne;
|
||||
import jakarta.persistence.Table;
|
||||
|
||||
@Entity
|
||||
@Table(name = "product")
|
||||
@Table(name = "products")
|
||||
public class ProductEntity extends BaseEntity {
|
||||
@ManyToOne
|
||||
@JoinColumn(name = "typeId", nullable = false)
|
||||
private TypeEntity type;
|
||||
@Column(nullable = false, length = 50)
|
||||
@Column(nullable = false)
|
||||
private String name;
|
||||
@Column(nullable = false, length = 50)
|
||||
@Column(nullable = false)
|
||||
private Double price;
|
||||
@Column(nullable = false)
|
||||
private String description;
|
||||
@ManyToOne
|
||||
@JoinColumn(name = "typeId", nullable = false, unique = false)
|
||||
private TypeEntity type;
|
||||
|
||||
public ProductEntity() {
|
||||
super();
|
||||
}
|
||||
|
||||
public ProductEntity(String name, TypeEntity type, Double price) {
|
||||
public ProductEntity(TypeEntity type, String name, Double price, String description) {
|
||||
this.type = type;
|
||||
this.name = name;
|
||||
this.price = price;
|
||||
|
||||
this.description = description;
|
||||
}
|
||||
|
||||
public TypeEntity getType() {
|
||||
@ -41,14 +45,6 @@ public class ProductEntity extends BaseEntity {
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public Double getPrice() {
|
||||
return price;
|
||||
}
|
||||
@ -57,12 +53,27 @@ public class ProductEntity extends BaseEntity {
|
||||
this.price = price;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(id, type, name, price);
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public String getDescription() {
|
||||
return description;
|
||||
}
|
||||
|
||||
public void setDescription(String description) {
|
||||
this.description = description;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(id, type, price, description, name);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unlikely-arg-type")
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (this == obj)
|
||||
@ -72,7 +83,8 @@ public class ProductEntity extends BaseEntity {
|
||||
final ProductEntity other = (ProductEntity) obj;
|
||||
return Objects.equals(other.getId(), id)
|
||||
&& Objects.equals(other.getType(), type)
|
||||
&& Objects.equals(other.getType(), name)
|
||||
&& Objects.equals(other.getPrice(), price);
|
||||
&& Objects.equals(other.getPrice(), price)
|
||||
&& Objects.equals(other.getDescription(), description)
|
||||
&& Objects.equals(other.getName(), name);
|
||||
}
|
||||
}
|
||||
|
@ -1,14 +1,35 @@
|
||||
package com.example.demo.products.repository;
|
||||
|
||||
import java.util.Optional;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
import org.springframework.data.domain.Page;
|
||||
import org.springframework.data.domain.Pageable;
|
||||
import org.springframework.data.jpa.repository.Query;
|
||||
import org.springframework.data.repository.CrudRepository;
|
||||
import org.springframework.data.repository.PagingAndSortingRepository;
|
||||
|
||||
import com.example.demo.products.model.ProductEntity;
|
||||
|
||||
public interface ProductRepository extends CrudRepository<ProductEntity, Long> {
|
||||
Optional<ProductEntity> findByNameIgnoreCase(String name);
|
||||
public interface ProductRepository
|
||||
extends CrudRepository<ProductEntity, Long>, PagingAndSortingRepository<ProductEntity, Long> {
|
||||
|
||||
@Query("select distinct p from ProductEntity p join fetch p.type t where p.id = ?1")
|
||||
Optional<ProductEntity> findOneById(long id);
|
||||
|
||||
@Query("select distinct p from ProductEntity p join fetch p.type t where t.id = ?1")
|
||||
Page<ProductEntity> findByTypeId(long typeId, Pageable pageable);
|
||||
|
||||
@Query("select distinct p from ProductEntity p join fetch p.type t where t.id = ?1")
|
||||
List<ProductEntity> findByTypeId(long typeId);
|
||||
|
||||
@Query("select distinct p from ProductEntity p join fetch p.type t")
|
||||
Page<ProductEntity> findAll(Pageable pageable);
|
||||
|
||||
@Query("select distinct p from ProductEntity p join fetch p.type t")
|
||||
List<ProductEntity> findAll();
|
||||
|
||||
@Query("SELECT p FROM OrderEntity o JOIN o.products p GROUP BY p.id ORDER BY COUNT(p.id) DESC")
|
||||
List<ProductEntity> findTop5MostPurchasedProducts(Pageable pageable);
|
||||
|
||||
List<ProductEntity> findByType_Id(Long typeId);
|
||||
}
|
@ -1,40 +1,62 @@
|
||||
package com.example.demo.products.service;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import org.springframework.stereotype.Service;
|
||||
import java.util.Objects;
|
||||
import java.util.stream.StreamSupport;
|
||||
|
||||
import org.springframework.data.domain.Page;
|
||||
import org.springframework.data.domain.PageRequest;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
import org.springframework.data.domain.Pageable;
|
||||
|
||||
import com.example.demo.core.error.NotFoundException;
|
||||
import com.example.demo.orders.repository.OrderRepository;
|
||||
import com.example.demo.products.model.ProductEntity;
|
||||
import com.example.demo.products.repository.ProductRepository;
|
||||
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
@Service
|
||||
public class ProductService {
|
||||
private final ProductRepository repository;
|
||||
private final OrderRepository orderRepository;
|
||||
|
||||
public ProductService(ProductRepository repository) {
|
||||
public ProductService(ProductRepository repository, OrderRepository orderRepository) {
|
||||
this.repository = repository;
|
||||
this.orderRepository = orderRepository;
|
||||
}
|
||||
|
||||
@Transactional(readOnly = true)
|
||||
public List<ProductEntity> getAll(Long typeId) {
|
||||
if (typeId != null && typeId != 0L) {
|
||||
return repository.findByType_Id(typeId);
|
||||
} else {
|
||||
return StreamSupport.stream(repository.findAll().spliterator(), false).toList();
|
||||
public List<ProductEntity> getAll(long typeId) {
|
||||
if (!Objects.equals(typeId, 0L)) {
|
||||
return repository.findByTypeId(typeId);
|
||||
}
|
||||
return repository.findAll();
|
||||
}
|
||||
|
||||
@Transactional(readOnly = true)
|
||||
public Page<ProductEntity> getAll(long typeId, int page, int size) {
|
||||
final Pageable pageRequest = PageRequest.of(page, size);
|
||||
|
||||
if (!Objects.equals(typeId, 0L)) {
|
||||
return repository.findByTypeId(typeId, pageRequest);
|
||||
}
|
||||
return repository.findAll(pageRequest);
|
||||
}
|
||||
|
||||
@Transactional(readOnly = true)
|
||||
public ProductEntity get(Long id) {
|
||||
return repository.findById(id).orElseThrow(() -> new NotFoundException(ProductEntity.class, id));
|
||||
return repository.findOneById(id).orElseThrow(() -> new NotFoundException(ProductEntity.class, id));
|
||||
}
|
||||
|
||||
@Transactional(readOnly = true)
|
||||
public List<ProductEntity> get(List<Long> id) {
|
||||
return StreamSupport.stream(repository.findAllById(id).spliterator(), false).toList();
|
||||
public List<ProductEntity> getByIds(Collection<Long> ids) {
|
||||
final List<ProductEntity> products = StreamSupport.stream(repository.findAllById(ids).spliterator(), false)
|
||||
.toList();
|
||||
if (products.size() < ids.size()) {
|
||||
throw new IllegalArgumentException("Invalid type");
|
||||
}
|
||||
return products;
|
||||
}
|
||||
|
||||
@Transactional
|
||||
@ -47,9 +69,12 @@ public class ProductService {
|
||||
|
||||
@Transactional
|
||||
public ProductEntity update(Long id, ProductEntity entity) {
|
||||
final ProductEntity exisEntity = get(id);
|
||||
exisEntity.setName(entity.getName());
|
||||
return repository.save(exisEntity);
|
||||
final ProductEntity existEntity = get(id);
|
||||
existEntity.setName(entity.getName());
|
||||
existEntity.setPrice(entity.getPrice());
|
||||
existEntity.setDescription(entity.getDescription());
|
||||
existEntity.setType(entity.getType());
|
||||
return repository.save(existEntity);
|
||||
}
|
||||
|
||||
@Transactional
|
||||
@ -58,4 +83,9 @@ public class ProductService {
|
||||
repository.delete(existEntity);
|
||||
return existEntity;
|
||||
}
|
||||
|
||||
public List<ProductEntity> getTopFiveMostSoldProducts() {
|
||||
Pageable topFive = PageRequest.of(0, 5);
|
||||
return repository.findTop5MostPurchasedProducts(topFive);
|
||||
}
|
||||
}
|
||||
|
@ -1,16 +1,14 @@
|
||||
package com.example.demo.types.api;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.modelmapper.ModelMapper;
|
||||
import org.springframework.web.bind.annotation.DeleteMapping;
|
||||
import org.springframework.stereotype.Controller;
|
||||
import org.springframework.ui.Model;
|
||||
import org.springframework.validation.BindingResult;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.ModelAttribute;
|
||||
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.RestController;
|
||||
|
||||
import com.example.demo.core.configuration.Constants;
|
||||
import com.example.demo.types.model.TypeEntity;
|
||||
@ -18,9 +16,15 @@ import com.example.demo.types.service.TypeService;
|
||||
|
||||
import jakarta.validation.Valid;
|
||||
|
||||
@RestController
|
||||
@RequestMapping(Constants.API_URL + "/type")
|
||||
@Controller
|
||||
@RequestMapping(TypeController.URL)
|
||||
public class TypeController {
|
||||
|
||||
public static final String URL = Constants.ADMIN_PREFIX + "/type";
|
||||
private static final String TYPE_VIEW = "type";
|
||||
private static final String TYPE_EDIT_VIEW = "type-edit";
|
||||
private static final String TYPE_ATTRIBUTE = "type";
|
||||
|
||||
private final TypeService typeService;
|
||||
private final ModelMapper modelMapper;
|
||||
|
||||
@ -38,27 +42,64 @@ public class TypeController {
|
||||
}
|
||||
|
||||
@GetMapping
|
||||
public List<TypeDto> getAll() {
|
||||
return typeService.getAll().stream().map(this::toDto).toList();
|
||||
public String getAll(Model model) {
|
||||
model.addAttribute(
|
||||
"items",
|
||||
typeService.getAll().stream()
|
||||
.map(this::toDto)
|
||||
.toList());
|
||||
return TYPE_VIEW;
|
||||
}
|
||||
|
||||
@GetMapping("/{id}")
|
||||
public TypeDto get(@PathVariable(name = "id") Long id) {
|
||||
return toDto(typeService.get(id));
|
||||
@GetMapping("/edit/")
|
||||
public String create(Model model) {
|
||||
model.addAttribute(TYPE_ATTRIBUTE, new TypeDto());
|
||||
return TYPE_EDIT_VIEW;
|
||||
}
|
||||
|
||||
@PostMapping
|
||||
public TypeDto create(@RequestBody @Valid TypeDto dto) {
|
||||
return toDto(typeService.create(toEntity(dto)));
|
||||
@PostMapping("/edit/")
|
||||
public String create(
|
||||
@ModelAttribute(name = TYPE_ATTRIBUTE) @Valid TypeDto type,
|
||||
BindingResult bindingResult,
|
||||
Model model) {
|
||||
if (bindingResult.hasErrors()) {
|
||||
return TYPE_EDIT_VIEW;
|
||||
}
|
||||
typeService.create(toEntity(type));
|
||||
return Constants.REDIRECT_VIEW + URL;
|
||||
}
|
||||
|
||||
@PutMapping("/{id}")
|
||||
public TypeDto update(@PathVariable(name = "id") Long id, @RequestBody TypeDto dto) {
|
||||
return toDto(typeService.update(id, toEntity(dto)));
|
||||
@GetMapping("/edit/{id}")
|
||||
public String update(
|
||||
@PathVariable(name = "id") Long id,
|
||||
Model model) {
|
||||
if (id <= 0) {
|
||||
throw new IllegalArgumentException();
|
||||
}
|
||||
model.addAttribute(TYPE_ATTRIBUTE, toDto(typeService.get(id)));
|
||||
return TYPE_EDIT_VIEW;
|
||||
}
|
||||
|
||||
@DeleteMapping("/{id}")
|
||||
public TypeDto delete(@PathVariable(name = "id") Long id) {
|
||||
return toDto(typeService.delete(id));
|
||||
@PostMapping("/edit/{id}")
|
||||
public String update(
|
||||
@PathVariable(name = "id") Long id,
|
||||
@ModelAttribute(name = TYPE_ATTRIBUTE) @Valid TypeDto type,
|
||||
BindingResult bindingResult,
|
||||
Model model) {
|
||||
if (bindingResult.hasErrors()) {
|
||||
return TYPE_EDIT_VIEW;
|
||||
}
|
||||
if (id <= 0) {
|
||||
throw new IllegalArgumentException();
|
||||
}
|
||||
typeService.update(id, toEntity(type));
|
||||
return Constants.REDIRECT_VIEW + URL;
|
||||
}
|
||||
|
||||
@PostMapping("/delete/{id}")
|
||||
public String delete(
|
||||
@PathVariable(name = "id") Long id) {
|
||||
typeService.delete(id);
|
||||
return Constants.REDIRECT_VIEW + URL;
|
||||
}
|
||||
}
|
||||
|
@ -1,17 +1,14 @@
|
||||
package com.example.demo.types.api;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
import jakarta.validation.constraints.Size;
|
||||
|
||||
public class TypeDto {
|
||||
private Long id;
|
||||
@NotBlank
|
||||
@Size(min = 5, max = 50)
|
||||
@Size(min = 1, max = 50)
|
||||
private String name;
|
||||
|
||||
@JsonProperty(access = JsonProperty.Access.READ_ONLY)
|
||||
public Long getId() {
|
||||
return id;
|
||||
}
|
||||
|
@ -3,45 +3,44 @@ package com.example.demo.types.model;
|
||||
import java.util.Objects;
|
||||
|
||||
import com.example.demo.core.model.BaseEntity;
|
||||
|
||||
import jakarta.persistence.Column;
|
||||
import jakarta.persistence.Entity;
|
||||
import jakarta.persistence.Table;
|
||||
|
||||
@Entity
|
||||
@Table(name = "types")
|
||||
public class TypeEntity extends BaseEntity {
|
||||
public class TypeEntity extends BaseEntity{
|
||||
@Column(nullable = false, unique = true, length = 50)
|
||||
private String name;
|
||||
|
||||
public TypeEntity() {
|
||||
public TypeEntity(){
|
||||
}
|
||||
|
||||
public TypeEntity(String name) {
|
||||
public TypeEntity(String name){
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
public String getName(){
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
public void setName(String name){
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(id, name);
|
||||
public int hashCode(){
|
||||
return Objects.hash(id,name);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (this == obj)
|
||||
public boolean equals(Object obj){
|
||||
if(this == obj)
|
||||
return true;
|
||||
if (obj == null || getClass() != obj.getClass())
|
||||
return false;
|
||||
final TypeEntity other = (TypeEntity) obj;
|
||||
return Objects.equals(other.getId(), id)
|
||||
&& Objects.equals(other.getName(), name);
|
||||
return Objects.equals(other.getId(), id) && Objects.equals(other.getName(), name);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -8,4 +8,5 @@ import com.example.demo.types.model.TypeEntity;
|
||||
|
||||
public interface TypeRepository extends CrudRepository<TypeEntity, Long> {
|
||||
Optional<TypeEntity> findByNameIgnoreCase(String name);
|
||||
Optional<TypeEntity> findById(long id);
|
||||
}
|
||||
|
@ -3,6 +3,7 @@ package com.example.demo.types.service;
|
||||
import java.util.List;
|
||||
import java.util.stream.StreamSupport;
|
||||
|
||||
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
@ -13,48 +14,38 @@ import com.example.demo.types.repository.TypeRepository;
|
||||
@Service
|
||||
public class TypeService {
|
||||
private final TypeRepository repository;
|
||||
|
||||
public TypeService(TypeRepository repository) {
|
||||
|
||||
public TypeService(TypeRepository repository){
|
||||
this.repository = repository;
|
||||
}
|
||||
|
||||
private void checkName(String name) {
|
||||
if (repository.findByNameIgnoreCase(name).isPresent()) {
|
||||
throw new IllegalArgumentException(
|
||||
String.format("Type with name %s is already exists", name));
|
||||
}
|
||||
}
|
||||
|
||||
@Transactional(readOnly = true)
|
||||
public List<TypeEntity> getAll() {
|
||||
public List<TypeEntity> getAll(){
|
||||
return StreamSupport.stream(repository.findAll().spliterator(), false).toList();
|
||||
}
|
||||
|
||||
@Transactional(readOnly = true)
|
||||
public TypeEntity get(long id) {
|
||||
return repository.findById(id)
|
||||
.orElseThrow(() -> new NotFoundException(TypeEntity.class, id));
|
||||
public TypeEntity get(Long id){
|
||||
return repository.findById(id).orElseThrow(() -> new NotFoundException(TypeEntity.class, id));
|
||||
}
|
||||
|
||||
@Transactional
|
||||
public TypeEntity create(TypeEntity entity) {
|
||||
if (entity == null) {
|
||||
public TypeEntity create(TypeEntity entity){
|
||||
if(entity == null){
|
||||
throw new IllegalArgumentException("Entity is null");
|
||||
}
|
||||
checkName(entity.getName());
|
||||
return repository.save(entity);
|
||||
}
|
||||
|
||||
@Transactional
|
||||
public TypeEntity update(Long id, TypeEntity entity) {
|
||||
public TypeEntity update(Long id, TypeEntity entity){
|
||||
final TypeEntity existsEntity = get(id);
|
||||
checkName(entity.getName());
|
||||
existsEntity.setName(entity.getName());
|
||||
return repository.save(existsEntity);
|
||||
}
|
||||
|
||||
@Transactional
|
||||
public TypeEntity delete(Long id) {
|
||||
public TypeEntity delete(Long id){
|
||||
final TypeEntity existsEntity = get(id);
|
||||
repository.delete(existsEntity);
|
||||
return existsEntity;
|
||||
|
114
src/main/java/com/example/demo/users/api/UserCartController.java
Normal file
114
src/main/java/com/example/demo/users/api/UserCartController.java
Normal file
@ -0,0 +1,114 @@
|
||||
package com.example.demo.users.api;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.springframework.security.core.annotation.AuthenticationPrincipal;
|
||||
import org.springframework.stereotype.Controller;
|
||||
import org.springframework.ui.Model;
|
||||
import org.springframework.validation.BindingResult;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.ModelAttribute;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.SessionAttributes;
|
||||
import org.springframework.web.bind.support.SessionStatus;
|
||||
|
||||
import com.example.demo.core.configuration.Constants;
|
||||
import com.example.demo.core.security.UserPrincipal;
|
||||
import com.example.demo.core.session.SessionCart;
|
||||
import com.example.demo.products.api.ProductDto;
|
||||
import com.example.demo.products.model.ProductEntity;
|
||||
import com.example.demo.products.service.ProductService;
|
||||
import com.example.demo.orders.model.OrderEntity;
|
||||
import com.example.demo.orders.service.OrderService;
|
||||
|
||||
import jakarta.validation.Valid;
|
||||
|
||||
@Controller
|
||||
@RequestMapping(UserCartController.URL)
|
||||
@SessionAttributes("products")
|
||||
public class UserCartController {
|
||||
public static final String URL = "/cart";
|
||||
private static final String ORDER_VIEW = "cart";
|
||||
private static final String ORDER_ATTRIBUTE = "order";
|
||||
private static final String CART_ATTRIBUTE = "cart";
|
||||
|
||||
private final ProductService productService;
|
||||
private final OrderService orderService;
|
||||
private final SessionCart cart;
|
||||
|
||||
public UserCartController(
|
||||
ProductService productService,
|
||||
OrderService orderService,
|
||||
SessionCart cart) {
|
||||
this.productService = productService;
|
||||
this.orderService = orderService;
|
||||
this.cart = cart;
|
||||
}
|
||||
|
||||
private ProductDto toProductDto(ProductEntity entity) {
|
||||
var dto = new ProductDto();
|
||||
dto.setId(entity.getId());
|
||||
dto.setDescription(entity.getDescription());
|
||||
dto.setName(entity.getName());
|
||||
dto.setPrice(entity.getPrice());
|
||||
dto.setTypeId(entity.getType().getId());
|
||||
return dto;
|
||||
}
|
||||
|
||||
private OrderEntity toOrderEntities(Collection<UserCartDto> dtos) {
|
||||
final Set<Long> productIds = dtos.stream()
|
||||
.map(UserCartDto::getProduct)
|
||||
.collect(Collectors.toSet());
|
||||
final List<ProductEntity> products = productService.getByIds(productIds);
|
||||
return new OrderEntity(products);
|
||||
}
|
||||
|
||||
@GetMapping
|
||||
public String getCart(Model model) {
|
||||
model.addAttribute("products",
|
||||
productService.getAll(0).stream()
|
||||
.map(this::toProductDto)
|
||||
.toList());
|
||||
model.addAttribute(ORDER_ATTRIBUTE, new UserCartDto());
|
||||
model.addAttribute(CART_ATTRIBUTE, cart.values());
|
||||
return ORDER_VIEW;
|
||||
}
|
||||
|
||||
@PostMapping
|
||||
public String addOrderToCart(
|
||||
@ModelAttribute(name = ORDER_ATTRIBUTE) @Valid UserCartDto order,
|
||||
BindingResult bindingResult,
|
||||
SessionStatus status,
|
||||
Model model) {
|
||||
if (bindingResult.hasErrors()) {
|
||||
return ORDER_VIEW;
|
||||
}
|
||||
status.setComplete();
|
||||
order.setProductName(productService.get(order.getProduct()).getName());
|
||||
order.setPrice(productService.get(order.getProduct()).getPrice());
|
||||
cart.computeIfPresent(order.hashCode(), (key, value) -> {
|
||||
return value;
|
||||
});
|
||||
cart.put(order.hashCode(), order);
|
||||
return Constants.REDIRECT_VIEW + URL;
|
||||
}
|
||||
|
||||
@PostMapping("/save")
|
||||
public String saveCart(
|
||||
Model model,
|
||||
@AuthenticationPrincipal UserPrincipal principal) {
|
||||
orderService.create(principal.getId(), toOrderEntities(cart.values()));
|
||||
cart.clear();
|
||||
return Constants.REDIRECT_VIEW + URL;
|
||||
}
|
||||
|
||||
@PostMapping("/clear")
|
||||
public String clearCart() {
|
||||
cart.clear();
|
||||
return Constants.REDIRECT_VIEW + URL;
|
||||
}
|
||||
}
|
53
src/main/java/com/example/demo/users/api/UserCartDto.java
Normal file
53
src/main/java/com/example/demo/users/api/UserCartDto.java
Normal file
@ -0,0 +1,53 @@
|
||||
package com.example.demo.users.api;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
import jakarta.validation.constraints.Min;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
|
||||
public class UserCartDto {
|
||||
@NotNull
|
||||
private Long product;
|
||||
private String productName;
|
||||
@Min(1000)
|
||||
private Double price;
|
||||
|
||||
public Long getProduct() {
|
||||
return product;
|
||||
}
|
||||
|
||||
public void setProduct(Long productId) {
|
||||
this.product = productId;
|
||||
}
|
||||
|
||||
public String getProductName() {
|
||||
return productName;
|
||||
}
|
||||
|
||||
public void setProductName(String productName) {
|
||||
this.productName = productName;
|
||||
}
|
||||
|
||||
public Double getPrice() {
|
||||
return price;
|
||||
}
|
||||
|
||||
public void setPrice(Double price) {
|
||||
this.price = price;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(product, price);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (this == obj)
|
||||
return true;
|
||||
if (obj == null || getClass() != obj.getClass())
|
||||
return false;
|
||||
UserCartDto other = (UserCartDto) obj;
|
||||
return Objects.equals(product, other.product) && Objects.equals(price, other.price);
|
||||
}
|
||||
}
|
@ -0,0 +1,98 @@
|
||||
package com.example.demo.users.api;
|
||||
|
||||
import org.springframework.stereotype.Controller;
|
||||
import org.springframework.ui.Model;
|
||||
import org.springframework.validation.BindingResult;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.ModelAttribute;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
import org.springframework.web.bind.support.SessionStatus;
|
||||
|
||||
import org.modelmapper.ModelMapper;
|
||||
|
||||
import com.example.demo.core.api.PageAttributesMapper;
|
||||
import com.example.demo.core.configuration.Constants;
|
||||
import com.example.demo.core.session.SessionCart;
|
||||
import com.example.demo.products.api.ProductDto;
|
||||
import com.example.demo.products.model.ProductEntity;
|
||||
import com.example.demo.products.service.ProductService;
|
||||
import com.example.demo.types.api.TypeDto;
|
||||
import com.example.demo.types.model.TypeEntity;
|
||||
import com.example.demo.types.service.TypeService;
|
||||
|
||||
import jakarta.validation.Valid;
|
||||
|
||||
@Controller
|
||||
@RequestMapping(UserCatalogController.URL)
|
||||
public class UserCatalogController {
|
||||
|
||||
private static final String CATALOG_VIEW = "catalog";
|
||||
public static final String URL = "/catalog";
|
||||
private static final String TYPEID_ATTRIBUTE = "typeId";
|
||||
private static final String PAGE_ATTRIBUTE = "page";
|
||||
private static final String ORDER_ATTRIBUTE = "order";
|
||||
|
||||
private final ProductService productService;
|
||||
private final TypeService typeService;
|
||||
private final ModelMapper modelMapper;
|
||||
private final SessionCart cart;
|
||||
|
||||
public UserCatalogController(ProductService productService, SessionCart cart, TypeService typeService,
|
||||
ModelMapper modelMapper) {
|
||||
this.productService = productService;
|
||||
this.modelMapper = modelMapper;
|
||||
this.typeService = typeService;
|
||||
this.cart = cart;
|
||||
}
|
||||
|
||||
private ProductDto toProductDto(ProductEntity entity) {
|
||||
var dto = new ProductDto();
|
||||
dto.setId(entity.getId());
|
||||
dto.setDescription(entity.getDescription());
|
||||
dto.setName(entity.getName());
|
||||
dto.setPrice(entity.getPrice());
|
||||
dto.setTypeId(entity.getType().getId());
|
||||
return dto;
|
||||
}
|
||||
|
||||
private TypeDto toTypeDto(TypeEntity entity) {
|
||||
return modelMapper.map(entity, TypeDto.class);
|
||||
}
|
||||
|
||||
@GetMapping
|
||||
public String getCatalog(
|
||||
@RequestParam(name = PAGE_ATTRIBUTE, defaultValue = "0") int page,
|
||||
@RequestParam(name = TYPEID_ATTRIBUTE, defaultValue = "0") int typeId,
|
||||
Model model) {
|
||||
model.addAttribute(ORDER_ATTRIBUTE, new UserCartDto());
|
||||
model.addAttribute(PAGE_ATTRIBUTE, page);
|
||||
model.addAllAttributes(PageAttributesMapper.toAttributes(
|
||||
productService.getAll(typeId, page, Constants.DEFAULT_PAGE_SIZE), this::toProductDto));
|
||||
model.addAttribute("types",
|
||||
typeService.getAll().stream()
|
||||
.map(this::toTypeDto)
|
||||
.toList());
|
||||
return CATALOG_VIEW;
|
||||
}
|
||||
|
||||
@PostMapping
|
||||
public String addOrderToCart(
|
||||
@ModelAttribute(name = ORDER_ATTRIBUTE) @Valid UserCartDto order,
|
||||
BindingResult bindingResult,
|
||||
SessionStatus status,
|
||||
Model model) {
|
||||
if (bindingResult.hasErrors()) {
|
||||
return CATALOG_VIEW;
|
||||
}
|
||||
status.setComplete();
|
||||
order.setProductName(productService.get(order.getProduct()).getName());
|
||||
order.setPrice(productService.get(order.getProduct()).getPrice());
|
||||
cart.computeIfPresent(order.hashCode(), (key, value) -> {
|
||||
return value;
|
||||
});
|
||||
cart.put(order.hashCode(), order);
|
||||
return Constants.REDIRECT_VIEW + URL;
|
||||
}
|
||||
}
|
@ -1,31 +1,45 @@
|
||||
package com.example.demo.users.api;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.modelmapper.ModelMapper;
|
||||
import org.springframework.web.bind.annotation.DeleteMapping;
|
||||
import org.springframework.stereotype.Controller;
|
||||
import org.springframework.ui.Model;
|
||||
import org.springframework.validation.BindingResult;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.ModelAttribute;
|
||||
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.RestController;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
import org.springframework.web.servlet.mvc.support.RedirectAttributes;
|
||||
|
||||
import com.example.demo.core.api.PageAttributesMapper;
|
||||
|
||||
import com.example.demo.core.configuration.Constants;
|
||||
import com.example.demo.orders.service.OrderService;
|
||||
import com.example.demo.users.model.UserEntity;
|
||||
import com.example.demo.users.service.UserService;
|
||||
|
||||
import jakarta.validation.Valid;
|
||||
|
||||
@RestController
|
||||
@RequestMapping(Constants.API_URL + "/user")
|
||||
@Controller
|
||||
@RequestMapping(UserController.URL)
|
||||
public class UserController {
|
||||
private final UserService userService;
|
||||
private final ModelMapper modelMapper;
|
||||
|
||||
public UserController(UserService userService, ModelMapper modelMapper) {
|
||||
this.userService = userService;
|
||||
public static final String URL = Constants.ADMIN_PREFIX + "/user";
|
||||
private static final String USER_VIEW = "user";
|
||||
private static final String USER_EDIT_VIEW = "user-edit";
|
||||
private static final String PAGE_ATTRIBUTE = "page";
|
||||
private static final String USER_ATTRIBUTE = "user";
|
||||
|
||||
private final ModelMapper modelMapper;
|
||||
private final UserService userService;
|
||||
|
||||
public UserController(OrderService orderService, ModelMapper modelMapper, UserService userService) {
|
||||
this.modelMapper = modelMapper;
|
||||
this.userService = userService;
|
||||
}
|
||||
|
||||
private UserDto toDto(UserEntity entity) {
|
||||
@ -37,26 +51,75 @@ public class UserController {
|
||||
}
|
||||
|
||||
@GetMapping
|
||||
public List<UserDto> getAll() {
|
||||
return userService.getAll().stream().map(this::toDto).toList();
|
||||
public String getAll(
|
||||
@RequestParam(name = PAGE_ATTRIBUTE, defaultValue = "0") int page,
|
||||
Model model) {
|
||||
final Map<String, Object> attributes = PageAttributesMapper.toAttributes(
|
||||
userService.getAll(page, Constants.DEFAULT_PAGE_SIZE), this::toDto);
|
||||
model.addAllAttributes(attributes);
|
||||
model.addAttribute(PAGE_ATTRIBUTE, page);
|
||||
return USER_VIEW;
|
||||
}
|
||||
|
||||
@GetMapping("/{id}")
|
||||
public UserDto get(@PathVariable(name = "id") Long id) {
|
||||
return toDto(userService.get(id));
|
||||
@GetMapping("/edit/")
|
||||
public String create(
|
||||
@RequestParam(name = PAGE_ATTRIBUTE, defaultValue = "0") int page,
|
||||
Model model) {
|
||||
model.addAttribute(USER_ATTRIBUTE, new UserDto());
|
||||
model.addAttribute(PAGE_ATTRIBUTE, page);
|
||||
return USER_EDIT_VIEW;
|
||||
}
|
||||
|
||||
@PostMapping
|
||||
public UserDto create(@RequestBody @Valid UserDto dto) {
|
||||
return toDto(userService.create(toEntity(dto)));
|
||||
@PostMapping("/edit/")
|
||||
public String create(
|
||||
@RequestParam(name = PAGE_ATTRIBUTE, defaultValue = "0") int page,
|
||||
@ModelAttribute(name = USER_ATTRIBUTE) @Valid UserDto user,
|
||||
BindingResult bindingResult,
|
||||
Model model,
|
||||
RedirectAttributes redirectAttributes) {
|
||||
if (bindingResult.hasErrors()) {
|
||||
model.addAttribute(PAGE_ATTRIBUTE, page);
|
||||
return USER_EDIT_VIEW;
|
||||
}
|
||||
redirectAttributes.addAttribute(PAGE_ATTRIBUTE, page);
|
||||
userService.create(toEntity(user));
|
||||
return Constants.REDIRECT_VIEW + URL;
|
||||
}
|
||||
|
||||
@PutMapping("/{id}")
|
||||
public UserDto update(@PathVariable(name = "id") Long id, @RequestBody UserDto dto) {
|
||||
return toDto(userService.update(id, toEntity(dto)));
|
||||
@GetMapping("/edit/{id}")
|
||||
public String update(
|
||||
@PathVariable(name = "id") Long id,
|
||||
@RequestParam(name = PAGE_ATTRIBUTE, defaultValue = "0") int page,
|
||||
Model model) {
|
||||
if (id <= 0) {
|
||||
throw new IllegalArgumentException();
|
||||
}
|
||||
model.addAttribute(USER_ATTRIBUTE, toDto(userService.get(id)));
|
||||
model.addAttribute(PAGE_ATTRIBUTE, page);
|
||||
return USER_EDIT_VIEW;
|
||||
}
|
||||
|
||||
@DeleteMapping("/{id}")
|
||||
@PostMapping("/edit/{id}")
|
||||
public String update(
|
||||
@PathVariable(name = "id") Long id,
|
||||
@RequestParam(name = PAGE_ATTRIBUTE, defaultValue = "0") int page,
|
||||
@ModelAttribute(name = USER_ATTRIBUTE) @Valid UserDto user,
|
||||
BindingResult bindingResult,
|
||||
Model model,
|
||||
RedirectAttributes redirectAttributes) {
|
||||
if (bindingResult.hasErrors()) {
|
||||
model.addAttribute(PAGE_ATTRIBUTE, page);
|
||||
return USER_EDIT_VIEW;
|
||||
}
|
||||
if (id <= 0) {
|
||||
throw new IllegalArgumentException();
|
||||
}
|
||||
redirectAttributes.addAttribute(PAGE_ATTRIBUTE, page);
|
||||
userService.update(id, toEntity(user));
|
||||
return Constants.REDIRECT_VIEW + URL;
|
||||
}
|
||||
|
||||
@PostMapping("/delete/{id}")
|
||||
public UserDto delete(@PathVariable(name = "id") Long id) {
|
||||
return toDto(userService.delete(id));
|
||||
}
|
||||
|
@ -1,27 +1,18 @@
|
||||
package com.example.demo.users.api;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
|
||||
import jakarta.validation.constraints.Email;
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
import jakarta.validation.constraints.Size;
|
||||
|
||||
public class UserDto {
|
||||
private Long id;
|
||||
|
||||
@NotBlank
|
||||
private String fullname;
|
||||
|
||||
@Size(min = 2, max = 20)
|
||||
private String login;
|
||||
@NotBlank
|
||||
private String surname;
|
||||
|
||||
@NotBlank
|
||||
@Email
|
||||
@Size(min = 2, max = 20)
|
||||
private String email;
|
||||
private String role;
|
||||
|
||||
@NotBlank
|
||||
private String password;
|
||||
|
||||
@JsonProperty(access = JsonProperty.Access.READ_ONLY)
|
||||
public Long getId() {
|
||||
return id;
|
||||
}
|
||||
@ -30,20 +21,12 @@ public class UserDto {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public String getFullname() {
|
||||
return fullname;
|
||||
public String getLogin() {
|
||||
return login;
|
||||
}
|
||||
|
||||
public void setFullname(String fullname) {
|
||||
this.fullname = fullname;
|
||||
}
|
||||
|
||||
public String getSurname() {
|
||||
return surname;
|
||||
}
|
||||
|
||||
public void setSurname(String surname) {
|
||||
this.surname = surname;
|
||||
public void setLogin(String login) {
|
||||
this.login = login;
|
||||
}
|
||||
|
||||
public String getEmail() {
|
||||
@ -54,11 +37,11 @@ public class UserDto {
|
||||
this.email = email;
|
||||
}
|
||||
|
||||
public String getPassword() {
|
||||
return password;
|
||||
public String getRole() {
|
||||
return role;
|
||||
}
|
||||
|
||||
public void setPassword(String password) {
|
||||
this.password = password;
|
||||
public void setRole(String role) {
|
||||
this.role = role;
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,82 @@
|
||||
package com.example.demo.users.api;
|
||||
|
||||
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.example.demo.core.api.PageAttributesMapper;
|
||||
import com.example.demo.core.configuration.Constants;
|
||||
import com.example.demo.core.security.UserPrincipal;
|
||||
import com.example.demo.products.api.ProductDto;
|
||||
import com.example.demo.products.model.ProductEntity;
|
||||
import com.example.demo.products.service.ProductService;
|
||||
import com.example.demo.orders.api.OrderDto;
|
||||
import com.example.demo.orders.model.OrderEntity;
|
||||
import com.example.demo.orders.service.OrderService;
|
||||
|
||||
@Controller
|
||||
public class UserProfileController {
|
||||
private static final String PROFILE_VIEW = "profile";
|
||||
|
||||
private static final String PAGE_ATTRIBUTE = "page";
|
||||
|
||||
private final OrderService orderService;
|
||||
private final ProductService productService;
|
||||
|
||||
public UserProfileController(
|
||||
OrderService orderService,
|
||||
ProductService productService) {
|
||||
this.orderService = orderService;
|
||||
this.productService = productService;
|
||||
}
|
||||
|
||||
private OrderDto toDto(OrderEntity entity) {
|
||||
var dto = new OrderDto();
|
||||
dto.setId(entity.getId());
|
||||
dto.setProducts(entity.getProducts().stream().map(ProductEntity::getId).toList());
|
||||
return dto;
|
||||
}
|
||||
|
||||
private ProductDto toProductDto(ProductEntity entity) {
|
||||
var dto = new ProductDto();
|
||||
dto.setId(entity.getId());
|
||||
dto.setDescription(entity.getDescription());
|
||||
dto.setName(entity.getName());
|
||||
dto.setPrice(entity.getPrice());
|
||||
dto.setTypeId(entity.getType().getId());
|
||||
return dto;
|
||||
}
|
||||
|
||||
@GetMapping
|
||||
public String getProfile(
|
||||
@RequestParam(name = PAGE_ATTRIBUTE, defaultValue = "0") int page,
|
||||
Model model,
|
||||
@AuthenticationPrincipal UserPrincipal principal) {
|
||||
final long userId = principal.getId();
|
||||
model.addAttribute(PAGE_ATTRIBUTE, page);
|
||||
model.addAllAttributes(PageAttributesMapper.toAttributes(
|
||||
orderService.getAll(userId, page, Constants.DEFAULT_PAGE_SIZE),
|
||||
this::toDto));
|
||||
model.addAttribute("products",
|
||||
productService.getAll(0).stream()
|
||||
.map(this::toProductDto)
|
||||
.toList());
|
||||
return PROFILE_VIEW;
|
||||
}
|
||||
|
||||
@PostMapping("/delete/{id}")
|
||||
public String deleteOrder(
|
||||
@PathVariable(name = "id") Long id,
|
||||
@RequestParam(name = PAGE_ATTRIBUTE, defaultValue = "0") int page,
|
||||
RedirectAttributes redirectAttributes,
|
||||
@AuthenticationPrincipal UserPrincipal principal) {
|
||||
redirectAttributes.addAttribute(PAGE_ATTRIBUTE, page);
|
||||
orderService.delete(principal.getId(), id);
|
||||
return Constants.REDIRECT_VIEW + "/";
|
||||
}
|
||||
}
|
@ -0,0 +1,63 @@
|
||||
package com.example.demo.users.api;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
import org.modelmapper.ModelMapper;
|
||||
import org.springframework.stereotype.Controller;
|
||||
import org.springframework.ui.Model;
|
||||
import org.springframework.validation.BindingResult;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.ModelAttribute;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
|
||||
import com.example.demo.core.configuration.Constants;
|
||||
import com.example.demo.users.model.UserEntity;
|
||||
import com.example.demo.users.service.UserService;
|
||||
|
||||
import jakarta.validation.Valid;
|
||||
|
||||
@Controller
|
||||
@RequestMapping(UserSignupController.URL)
|
||||
public class UserSignupController {
|
||||
public static final String URL = "/signup";
|
||||
|
||||
private static final String SIGNUP_VIEW = "signup";
|
||||
private static final String USER_ATTRIBUTE = "user";
|
||||
|
||||
private final UserService userService;
|
||||
private final ModelMapper modelMapper;
|
||||
|
||||
public UserSignupController(
|
||||
UserService userService,
|
||||
ModelMapper modelMapper) {
|
||||
this.userService = userService;
|
||||
this.modelMapper = modelMapper;
|
||||
}
|
||||
|
||||
private UserEntity toEntity(UserSignupDto dto) {
|
||||
return modelMapper.map(dto, UserEntity.class);
|
||||
}
|
||||
|
||||
@GetMapping
|
||||
public String getSignup(Model model) {
|
||||
model.addAttribute(USER_ATTRIBUTE, new UserSignupDto());
|
||||
return SIGNUP_VIEW;
|
||||
}
|
||||
@PostMapping
|
||||
public String signup(
|
||||
@ModelAttribute(name = USER_ATTRIBUTE) @Valid UserSignupDto user,
|
||||
BindingResult bindingResult,
|
||||
Model model) {
|
||||
if (bindingResult.hasErrors()) {
|
||||
return SIGNUP_VIEW;
|
||||
}
|
||||
if (!Objects.equals(user.getPassword(), user.getPasswordConfirm())) {
|
||||
bindingResult.rejectValue("password", "signup:passwords", "Пароли не совпадают.");
|
||||
model.addAttribute(USER_ATTRIBUTE, user);
|
||||
return SIGNUP_VIEW;
|
||||
}
|
||||
userService.create(toEntity(user));
|
||||
return Constants.REDIRECT_VIEW + Constants.LOGIN_URL + "?signup";
|
||||
}
|
||||
}
|
51
src/main/java/com/example/demo/users/api/UserSignupDto.java
Normal file
51
src/main/java/com/example/demo/users/api/UserSignupDto.java
Normal file
@ -0,0 +1,51 @@
|
||||
package com.example.demo.users.api;
|
||||
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
import jakarta.validation.constraints.Size;
|
||||
|
||||
public class UserSignupDto {
|
||||
@NotBlank
|
||||
@Size(min = 3, max = 20)
|
||||
private String login;
|
||||
@NotBlank
|
||||
@Size(min = 3, max = 20)
|
||||
private String email;
|
||||
@NotBlank
|
||||
@Size(min = 3, max = 20)
|
||||
private String password;
|
||||
@NotBlank
|
||||
@Size(min = 3, max = 20)
|
||||
private String passwordConfirm;
|
||||
|
||||
public String getLogin() {
|
||||
return login;
|
||||
}
|
||||
|
||||
public void setLogin(String login) {
|
||||
this.login = login;
|
||||
}
|
||||
|
||||
public String getEmail() {
|
||||
return email;
|
||||
}
|
||||
|
||||
public void setEmail(String email) {
|
||||
this.email = email;
|
||||
}
|
||||
|
||||
public String getPassword() {
|
||||
return password;
|
||||
}
|
||||
|
||||
public void setPassword(String password) {
|
||||
this.password = password;
|
||||
}
|
||||
|
||||
public String getPasswordConfirm() {
|
||||
return passwordConfirm;
|
||||
}
|
||||
|
||||
public void setPasswordConfirm(String passwordConfirm) {
|
||||
this.passwordConfirm = passwordConfirm;
|
||||
}
|
||||
}
|
@ -1,84 +1,79 @@
|
||||
package com.example.demo.users.model;
|
||||
|
||||
import java.util.Objects;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import com.example.demo.core.model.BaseEntity;
|
||||
import com.example.demo.orders.model.OrderEntity;
|
||||
|
||||
import jakarta.persistence.Table;
|
||||
import jakarta.persistence.CascadeType;
|
||||
import jakarta.persistence.Column;
|
||||
import jakarta.persistence.Entity;
|
||||
import jakarta.persistence.OneToMany;
|
||||
import jakarta.persistence.OrderBy;
|
||||
import jakarta.persistence.Table;
|
||||
|
||||
@Entity
|
||||
@Table(name = "users")
|
||||
public class UserEntity extends BaseEntity {
|
||||
@Column(nullable = false, length = 50)
|
||||
private String fullname;
|
||||
@Column(nullable = false, length = 50)
|
||||
private String surname;
|
||||
@Column(nullable = false, unique = true, length = 50)
|
||||
public class UserEntity extends BaseEntity{
|
||||
@Column(nullable = false, unique = true, length = 20)
|
||||
private String login;
|
||||
@Column(nullable = false, unique = true, length = 20)
|
||||
private String email;
|
||||
@Column(nullable = false, length = 50)
|
||||
@Column(nullable = false, length = 60)
|
||||
private String password;
|
||||
private UserRole role;
|
||||
@OneToMany(mappedBy = "user", cascade = CascadeType.ALL)
|
||||
@OrderBy("id ASC")
|
||||
private List<OrderEntity> orders = new ArrayList<>();
|
||||
|
||||
public UserEntity() {
|
||||
super();
|
||||
public UserEntity(){
|
||||
}
|
||||
|
||||
public UserEntity(String fullname, String surname, String email, String password) {
|
||||
this.fullname = fullname;
|
||||
this.surname = surname;
|
||||
public UserEntity(String login, String email, String password){
|
||||
this.login = login;
|
||||
this.email = email;
|
||||
this.password = password;
|
||||
this.role = UserRole.USER;
|
||||
}
|
||||
|
||||
public String getFullname() {
|
||||
return fullname;
|
||||
public String getLogin(){
|
||||
return login;
|
||||
}
|
||||
public void setLogin(String login){
|
||||
this.login = login;
|
||||
}
|
||||
|
||||
public void setFullname(String fullname) {
|
||||
this.fullname = fullname;
|
||||
}
|
||||
|
||||
public String getSurname() {
|
||||
return surname;
|
||||
}
|
||||
|
||||
public void setSurname(String surname) {
|
||||
this.surname = surname;
|
||||
}
|
||||
|
||||
public String getEmail() {
|
||||
public String getEmail(){
|
||||
return email;
|
||||
}
|
||||
|
||||
public void setEmail(String email) {
|
||||
public void setEmail(String email){
|
||||
this.email = email;
|
||||
}
|
||||
|
||||
public String getPassword() {
|
||||
public String getPassword(){
|
||||
return password;
|
||||
}
|
||||
|
||||
public void setPassword(String password) {
|
||||
public void setPassword(String password){
|
||||
this.password = password;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(id, fullname, surname, email, password);
|
||||
public UserRole getRole() {
|
||||
return role;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (this == obj)
|
||||
return true;
|
||||
if (obj == null || getClass() != obj.getClass())
|
||||
return false;
|
||||
final UserEntity other = (UserEntity) obj;
|
||||
return Objects.equals(other.getId(), id)
|
||||
&& Objects.equals(other.getFullname(), fullname)
|
||||
&& Objects.equals(other.getSurname(), surname)
|
||||
&& Objects.equals(other.getEmail(), email)
|
||||
&& Objects.equals(other.getPassword(), password);
|
||||
public void setRole(UserRole role) {
|
||||
this.role = role;
|
||||
}
|
||||
|
||||
public List<OrderEntity> getOrders(){
|
||||
return orders;
|
||||
}
|
||||
|
||||
public void addOrder(OrderEntity order){
|
||||
if(order.getUser() != this){
|
||||
order.setUser(this);
|
||||
}
|
||||
orders.add(order);
|
||||
}
|
||||
}
|
||||
|
15
src/main/java/com/example/demo/users/model/UserRole.java
Normal file
15
src/main/java/com/example/demo/users/model/UserRole.java
Normal file
@ -0,0 +1,15 @@
|
||||
package com.example.demo.users.model;
|
||||
|
||||
import org.springframework.security.core.GrantedAuthority;
|
||||
|
||||
public enum UserRole implements GrantedAuthority{
|
||||
ADMIN,
|
||||
USER;
|
||||
|
||||
private static final String PREFIX = "ROLE_";
|
||||
|
||||
@Override
|
||||
public String getAuthority() {
|
||||
return PREFIX + this.name();
|
||||
}
|
||||
}
|
@ -8,5 +8,5 @@ import org.springframework.data.repository.PagingAndSortingRepository;
|
||||
import com.example.demo.users.model.UserEntity;
|
||||
|
||||
public interface UserRepository extends CrudRepository<UserEntity, Long>, PagingAndSortingRepository<UserEntity, Long> {
|
||||
Optional<UserEntity> findByEmailIgnoreCase(String email);
|
||||
}
|
||||
Optional<UserEntity> findByLoginIgnoreCase(String login);
|
||||
}
|
||||
|
@ -1,27 +1,43 @@
|
||||
package com.example.demo.users.service;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.stream.StreamSupport;
|
||||
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.stream.StreamSupport;
|
||||
|
||||
import org.springframework.data.domain.Page;
|
||||
import org.springframework.data.domain.Sort;
|
||||
import org.springframework.security.core.userdetails.UserDetails;
|
||||
import org.springframework.security.core.userdetails.UserDetailsService;
|
||||
import org.springframework.security.core.userdetails.UsernameNotFoundException;
|
||||
import org.springframework.security.crypto.password.PasswordEncoder;
|
||||
import org.springframework.data.domain.PageRequest;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
import com.example.demo.core.configuration.Constants;
|
||||
import com.example.demo.core.error.NotFoundException;
|
||||
import com.example.demo.core.security.UserPrincipal;
|
||||
import com.example.demo.users.model.UserEntity;
|
||||
import com.example.demo.users.model.UserRole;
|
||||
import com.example.demo.users.repository.UserRepository;
|
||||
|
||||
@Service
|
||||
public class UserService {
|
||||
public class UserService implements UserDetailsService {
|
||||
private final UserRepository repository;
|
||||
private final PasswordEncoder passwordEncoder;
|
||||
|
||||
public UserService(UserRepository repository) {
|
||||
public UserService(UserRepository repository, PasswordEncoder passwordEncoder) {
|
||||
this.repository = repository;
|
||||
this.passwordEncoder = passwordEncoder;
|
||||
}
|
||||
|
||||
private void checkEmail(String email) {
|
||||
if (repository.findByEmailIgnoreCase(email).isPresent()) {
|
||||
private void checkLogin(Long id, String login) {
|
||||
final Optional<UserEntity> existsUser = repository.findByLoginIgnoreCase(login);
|
||||
if (existsUser.isPresent() && !existsUser.get().getId().equals(id)) {
|
||||
throw new IllegalArgumentException(
|
||||
String.format("User with email %s is already exists", email));
|
||||
String.format("User with login %s is already exists", login));
|
||||
}
|
||||
}
|
||||
|
||||
@ -31,9 +47,19 @@ public class UserService {
|
||||
}
|
||||
|
||||
@Transactional(readOnly = true)
|
||||
public UserEntity get(long id) {
|
||||
return repository.findById(id)
|
||||
.orElseThrow(() -> new NotFoundException(UserEntity.class, id));
|
||||
public Page<UserEntity> getAll(int page, int size) {
|
||||
return repository.findAll(PageRequest.of(page, size, Sort.by("id")));
|
||||
}
|
||||
|
||||
@Transactional(readOnly = true)
|
||||
public UserEntity get(Long id) {
|
||||
return repository.findById(id).orElseThrow(() -> new NotFoundException(UserEntity.class, id));
|
||||
}
|
||||
|
||||
@Transactional(readOnly = true)
|
||||
public UserEntity getByLogin(String login) {
|
||||
return repository.findByLoginIgnoreCase(login)
|
||||
.orElseThrow(() -> new IllegalArgumentException("Invalid login"));
|
||||
}
|
||||
|
||||
@Transactional
|
||||
@ -41,18 +67,22 @@ public class UserService {
|
||||
if (entity == null) {
|
||||
throw new IllegalArgumentException("Entity is null");
|
||||
}
|
||||
checkEmail(entity.getEmail());
|
||||
checkLogin(null, entity.getLogin());
|
||||
final String password = Optional.ofNullable(entity.getPassword()).orElse("");
|
||||
entity.setPassword(
|
||||
passwordEncoder.encode(
|
||||
StringUtils.hasText(password.strip()) ? password : Constants.DEFAULT_PASSWORD));
|
||||
entity.setRole(Optional.ofNullable(entity.getRole()).orElse(UserRole.USER));
|
||||
repository.save(entity);
|
||||
return repository.save(entity);
|
||||
}
|
||||
|
||||
@Transactional
|
||||
public UserEntity update(Long id, UserEntity entity) {
|
||||
checkEmail(entity.getEmail());
|
||||
final UserEntity existEntity = get(id);
|
||||
existEntity.setFullname(entity.getFullname());
|
||||
existEntity.setSurname(entity.getSurname());
|
||||
checkLogin(id, entity.getLogin());
|
||||
existEntity.setLogin(entity.getLogin());
|
||||
existEntity.setEmail(entity.getEmail());
|
||||
existEntity.setPassword(entity.getPassword());
|
||||
repository.save(existEntity);
|
||||
return existEntity;
|
||||
}
|
||||
@ -63,4 +93,11 @@ public class UserService {
|
||||
repository.delete(existEntity);
|
||||
return existEntity;
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(readOnly = true)
|
||||
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
|
||||
final UserEntity existsUser = getByLogin(username);
|
||||
return new UserPrincipal(existsUser);
|
||||
}
|
||||
}
|
||||
|
5
src/main/resources/public/css/favicon.svg
Normal file
5
src/main/resources/public/css/favicon.svg
Normal file
@ -0,0 +1,5 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor"
|
||||
class="bi bi-emoji-sunglasses-fill" viewBox="0 0 16 16">
|
||||
<path
|
||||
d="M8 16A8 8 0 1 0 8 0a8 8 0 0 0 0 16M2.31 5.243A1 1 0 0 1 3.28 4H6a1 1 0 0 1 1 1v.116A4.2 4.2 0 0 1 8 5c.35 0 .69.04 1 .116V5a1 1 0 0 1 1-1h2.72a1 1 0 0 1 .97 1.243l-.311 1.242A2 2 0 0 1 11.439 8H11a2 2 0 0 1-1.994-1.839A3 3 0 0 0 8 6c-.393 0-.74.064-1.006.161A2 2 0 0 1 5 8h-.438a2 2 0 0 1-1.94-1.515zM4.969 9.75A3.5 3.5 0 0 0 8 11.5a3.5 3.5 0 0 0 3.032-1.75.5.5 0 1 1 .866.5A4.5 4.5 0 0 1 8 12.5a4.5 4.5 0 0 1-3.898-2.25.5.5 0 0 1 .866-.5z" />
|
||||
</svg>
|
After Width: | Height: | Size: 615 B |
BIN
src/main/resources/public/kola.jpg
Normal file
BIN
src/main/resources/public/kola.jpg
Normal file
Binary file not shown.
After Width: | Height: | Size: 86 KiB |
BIN
src/main/resources/public/pizza.jpg
Normal file
BIN
src/main/resources/public/pizza.jpg
Normal file
Binary file not shown.
After Width: | Height: | Size: 243 KiB |
48
src/main/resources/templates/cart.html
Normal file
48
src/main/resources/templates/cart.html
Normal file
@ -0,0 +1,48 @@
|
||||
<!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" class="w-50 mx-auto py-3">
|
||||
<strong class="flex-fill">Корзина</strong>
|
||||
<div class="d-flex flex-column align-items-center">
|
||||
<div class="card col-12 col-md-8 col-lg-6 w-75 align-items-center" th:each="cartItem : ${cart}">
|
||||
<div class="card-body col-12 p-2 d-flex flex-row align-items-center justify-content-center">
|
||||
<div class="col-5"><img src="/kola.jpg" class="w-75"></div>
|
||||
<div class="col-3">
|
||||
Название: [[${cartItem.productName}]]
|
||||
</div>
|
||||
<div class="col-4">
|
||||
Цена: [[${cartItem.Price}]]
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class=" mb-2 col-12 col-md-8 col-lg-6 d-flex justify-content-end">
|
||||
|
||||
</div>
|
||||
<div class="mb-2 col-12 col-md-8 col-lg-6 d-flex justify-content-center"
|
||||
th:if="${not #lists.isEmpty(cart)}">
|
||||
<form action="#" th:action="@{/cart/save}" method="post">
|
||||
<button type="submit" class="btn btn-primary" onclick="return confirm('Вы уверены?')">
|
||||
Оформить заказ
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
<div class="mb-2 col-12 col-md-8 col-lg-6 d-flex align-items-center">
|
||||
<form action="#" th:action="@{/cart/clear}" method="post">
|
||||
<button type="submit" class="btn btn-danger button-fixed-width"
|
||||
onclick="return confirm('Вы уверены?')">
|
||||
<i class="bi bi-x-lg"></i> Очистить
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
|
||||
</html>
|
72
src/main/resources/templates/catalog.html
Normal file
72
src/main/resources/templates/catalog.html
Normal file
@ -0,0 +1,72 @@
|
||||
<!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" class="w-50 mx-auto main">
|
||||
<div class="tab-content mt-2">
|
||||
<h2 class="text-center">Меню</h2>
|
||||
<div class="row mx-auto">
|
||||
<div class="pt-4 ps-2 text-center" style="font-size: larger;">Обязательно возьми</div>
|
||||
<div><img class="p-2" src="/kola.jpg" width="100%" /></div>
|
||||
</div>
|
||||
<div class="tab-pane container active table-responsive" id="orders">
|
||||
<form th:action="@{/catalog}" method="get" class="row mt-2 w-50 mx-auto pb-3">
|
||||
<div class="pb-3">
|
||||
<input type="hidden" th:name="page" th:value="${page}">
|
||||
<select th:name="typeId" id="typeId" class="form-select">
|
||||
<option selected value="">Фильтр по продукции</option>
|
||||
<option th:each="type : ${types}" th:value="${type.id}" th:selected="${type.id==typeId}">
|
||||
[[${type.name}]]
|
||||
</option>
|
||||
</select>
|
||||
<input type="hidden" th:name="page" th:value="${page}">
|
||||
</div>
|
||||
<button type="submit" class="btn btn-primary">Показать</button>
|
||||
</form>
|
||||
<table class="table">
|
||||
<thead>
|
||||
<th class="w-25"></th>
|
||||
<th>Название</th>
|
||||
<th>Тип</th>
|
||||
<th class="w-auto">Описание</th>
|
||||
<th>Цена</th>
|
||||
<th></th>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr th:each="producte : ${items}">
|
||||
<th scope="row"><img src="/kola.jpg" class="w-100"></th>
|
||||
<th scope="row" th:text="${producte.name}"></th>
|
||||
<th:block th:each="type : ${types}">
|
||||
<th:block th:if="${type.Id} eq ${producte.typeId}">
|
||||
<th scope="row" th:text="${type.name}"></th>
|
||||
</th:block>
|
||||
</th:block>
|
||||
<th scope="row" th:text="${producte.description}"></th>
|
||||
<th scope="row" th:text="${producte.Price}"></th>
|
||||
<th>
|
||||
<form th:action="@{/catalog}" th:object="${order}" method="post">
|
||||
<input type="hidden" th:name="page" th:value="${page}">
|
||||
<select hidden="hidden" th:field="*{product}" class="form-select">
|
||||
<option th:value="${producte.id}"></option>
|
||||
</select>
|
||||
<button type="submit" class="btn btn-link button-link"><i
|
||||
class="bi bi-cart2 d-inline-block align-top me-1 logo"></i></button>
|
||||
</form>
|
||||
</th>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<th:block th:replace="~{ pagination :: pagination (
|
||||
url='catalog',
|
||||
totalPages=${totalPages},
|
||||
currentPage=${currentPage}) }" />
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
</body>
|
||||
|
||||
</html>
|
@ -7,7 +7,7 @@
|
||||
<meta charset="UTF-8" />
|
||||
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title layout:title-pattern="$LAYOUT_TITLE - $CONTENT_TITLE">My shop</title>
|
||||
<title layout:title-pattern="$LAYOUT_TITLE - $CONTENT_TITLE">FasterPizza.ru</title>
|
||||
<script type="text/javascript" src="/webjars/bootstrap/5.3.3/dist/js/bootstrap.bundle.min.js"></script>
|
||||
<link rel="stylesheet" href="/webjars/bootstrap/5.3.3/dist/css/bootstrap.min.css" />
|
||||
<link rel="stylesheet" href="/webjars/bootstrap-icons/1.11.3/font/bootstrap-icons.min.css" />
|
||||
@ -18,8 +18,8 @@
|
||||
<nav class="navbar navbar-expand-md my-navbar" data-bs-theme="dark">
|
||||
<div class="container-fluid">
|
||||
<a class="navbar-brand" href="/">
|
||||
<i class="bi bi-cart2 d-inline-block align-top me-1 logo"></i>
|
||||
Туда сюда и пицца
|
||||
<i class="bi-pizza-slice d-inline-block align-top me-1 logo"></i>
|
||||
БыстроПицца
|
||||
</a>
|
||||
<th:block sec:authorize="isAuthenticated()" th:with="userName=${#authentication.name}">
|
||||
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#main-navbar"
|
||||
@ -35,16 +35,25 @@
|
||||
</a>
|
||||
<a class="nav-link" href="/admin/type"
|
||||
th:classappend="${activeLink.startsWith('/admin/type') ? 'active' : ''}">
|
||||
Типы заказов
|
||||
Типы Продукции
|
||||
</a>
|
||||
<a class="nav-link" href="/admin/subscription"
|
||||
th:classappend="${activeLink.startsWith('/admin/subscription') ? 'active' : ''}">
|
||||
Списки рассылки
|
||||
<a class="nav-link" href="/admin/product"
|
||||
th:classappend="${activeLink.startsWith('/admin/product') ? 'active' : ''}">
|
||||
Продукция
|
||||
</a>
|
||||
<a class="nav-link" href="/admin/product/top"
|
||||
th:classappend="${activeLink.startsWith('/admin/product/top') ? 'active' : ''}">
|
||||
Топ Продуктов
|
||||
</a>
|
||||
<a class="nav-link" href="/h2-console/" target="_blank">Консоль H2</a>
|
||||
</th:block>
|
||||
<a class="nav-link" href="/123" target="_blank">Ошибка 1</a>
|
||||
<a class="nav-link" href="/admin/123" target="_blank">Ошибка 2</a>
|
||||
<a class="nav-link" href="/catalog"
|
||||
th:classappend="${activeLink.startsWith('/catalog') ? 'active' : ''}">
|
||||
Каталог
|
||||
</a>
|
||||
<a class="nav-link" href="/">
|
||||
Заказы
|
||||
</a>
|
||||
</ul>
|
||||
<ul class="navbar-nav" th:if="${not #strings.isEmpty(userName)}">
|
||||
<form th:action="@{/logout}" method="post">
|
||||
@ -64,7 +73,7 @@
|
||||
<main class="container-fluid p-2" layout:fragment="content">
|
||||
</main>
|
||||
<footer class="my-footer mt-auto d-flex flex-shrink-0 justify-content-center align-items-center">
|
||||
Автор, [[${#dates.year(#dates.createNow())}]]
|
||||
Крюков А.И., [[${#dates.year(#dates.createNow())}]]
|
||||
</footer>
|
||||
</body>
|
||||
|
||||
|
37
src/main/resources/templates/error.html
Normal file
37
src/main/resources/templates/error.html
Normal file
@ -0,0 +1,37 @@
|
||||
<!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">
|
||||
<ul class="list-group mb-2">
|
||||
<th:block th:if="${#strings.isEmpty(message)}">
|
||||
<li class="list-group-item">
|
||||
Неизвестная ошибка
|
||||
</li>
|
||||
</th:block>
|
||||
<th:block th:if="${not #strings.isEmpty(message)}">
|
||||
<li class="list-group-item">
|
||||
<strong>Ошибка:</strong> [[${message}]]
|
||||
</li>
|
||||
</th:block>
|
||||
<th:block th:if="${not #strings.isEmpty(url)}">
|
||||
<li class="list-group-item">
|
||||
<strong>Адрес:</strong> [[${url}]]
|
||||
</li>
|
||||
<li class="list-group-item">
|
||||
<strong>Класс исключения:</strong> [[${exception}]]
|
||||
</li>
|
||||
<li class="list-group-item">
|
||||
[[${method}]] ([[${file}]]:[[${line}]])
|
||||
</li>
|
||||
</th:block>
|
||||
</ul>
|
||||
<a class="btn btn-primary button-fixed-width" href="/">На главную</a>
|
||||
</main>
|
||||
</body>
|
||||
|
||||
</html>
|
@ -7,7 +7,7 @@
|
||||
|
||||
<body>
|
||||
<main layout:fragment="content">
|
||||
<form action="#" th:action="@{/login}" method="post">
|
||||
<form action="#" th:action="@{/login}" method="post" class="w-25 mx-auto">
|
||||
<div th:if="${param.error}" class="alert alert-danger">
|
||||
Неверный логин или пароль
|
||||
</div>
|
||||
@ -39,6 +39,4 @@
|
||||
</main>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
|
||||
</html>
|
70
src/main/resources/templates/orders.html
Normal file
70
src/main/resources/templates/orders.html
Normal file
@ -0,0 +1,70 @@
|
||||
<!DOCTYPE html>
|
||||
<html xmlns:th="http://www.thymeleaf.org">
|
||||
|
||||
<body>
|
||||
<th:block th:fragment="orders (items, totalPages, currentPage, products)">
|
||||
<th:block th:switch="${items.size()}">
|
||||
<h2 th:case="0">Данные отсутствуют</h2>
|
||||
<th:block th:case="*" class="w-50 mx-auto">
|
||||
|
||||
<table class="table mt-2">
|
||||
<caption></caption>
|
||||
<thead>
|
||||
<tr>
|
||||
<th scope="col" class="w-10">ID</th>
|
||||
<th scope="col" class="w-auto">Игры</th>
|
||||
<th scope="col" class="w-10"></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr th:each="order : ${items}">
|
||||
<th scope="row" th:text="${order.id}"></th>
|
||||
<td>
|
||||
<table class="table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th scope="col" class="w-10"></th>
|
||||
<th scope="col" class="w-25"></th>
|
||||
<th scope="col" class="w-auto">Название продукции</th>
|
||||
<th scope="col" class="w-10">Цена</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr th:each="productId : ${order.products}">
|
||||
<th:block th:each="product : ${products}">
|
||||
<th:block th:if="${productId} eq ${product.id}">
|
||||
<th scope="row" th:text="${productId}"></th>
|
||||
<th scope="row"><img src="/kola.jpg" class="w-100">
|
||||
</th>
|
||||
<th scope="row" th:text="${product.name}"></th>
|
||||
<th scope="row" th:text="${product.price}"></th>
|
||||
</th:block>
|
||||
</th:block>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</td>
|
||||
<td>
|
||||
<form th:action="@{/delete/{id}(id=${order.id})}" method="post">
|
||||
<input type="hidden" th:name="page" th:value="${page}">
|
||||
<button type="submit" class="btn btn-link button-link"
|
||||
onclick="return confirm('Вы уверены?')">Удалить</button>
|
||||
</form>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</th:block>
|
||||
<th:block th:replace="~{ pagination :: pagination (
|
||||
url='',
|
||||
totalPages=${totalPages},
|
||||
currentPage=${currentPage}) }" />
|
||||
<div class="mb-2 d-flex justify-content-center">
|
||||
<a class="btn btn-primary" href="/cart">Создать заказ</a>
|
||||
</div>
|
||||
</th:block>
|
||||
</th:block>
|
||||
|
||||
</body>
|
||||
|
||||
</html>
|
51
src/main/resources/templates/pagination.html
Normal file
51
src/main/resources/templates/pagination.html
Normal file
@ -0,0 +1,51 @@
|
||||
<!DOCTYPE html>
|
||||
<html xmlns:th="http://www.thymeleaf.org">
|
||||
|
||||
<body>
|
||||
|
||||
<th:block th:fragment="pagination (url, totalPages, currentPage)">
|
||||
<nav th:if="${totalPages > 1}" th:with="
|
||||
maxPage=2,
|
||||
currentPage=${currentPage + 1}">
|
||||
<ul class="pagination justify-content-center"
|
||||
th:with="
|
||||
seqFrom=${currentPage - maxPage < 1 ? 1 : currentPage - maxPage},
|
||||
seqTo=${currentPage + maxPage > totalPages ? totalPages : currentPage + maxPage}">
|
||||
<th:block th:if="${currentPage > maxPage + 1}">
|
||||
<li class="page-item">
|
||||
<a class="page-link" aria-label="Previous" th:href="@{/{url}?page=0(url=${url})}">
|
||||
<span aria-hidden="true">«</span>
|
||||
</a>
|
||||
</li>
|
||||
<li class="page-item disabled">
|
||||
<span class="page-link" aria-label="Previous">
|
||||
<span aria-hidden="true">…</span>
|
||||
</span>
|
||||
</li>
|
||||
</th:block>
|
||||
<li class="page-item" th:each="page : ${#numbers.sequence(seqFrom, seqTo)}"
|
||||
th:classappend="${page == currentPage} ? 'active' : ''">
|
||||
<a class=" page-link" th:href="@{/{url}?page={page}(url=${url},page=${page - 1})}">
|
||||
<span th:text="${page}" />
|
||||
</a>
|
||||
</li>
|
||||
<th:block th:if="${currentPage < totalPages - maxPage}">
|
||||
<li class="page-item disabled">
|
||||
<span class="page-link" aria-label="Previous">
|
||||
<span aria-hidden="true">…</span>
|
||||
</span>
|
||||
</li>
|
||||
<li class="page-item">
|
||||
<a class="page-link" aria-label="Next"
|
||||
th:href="@{/{url}?page={page}(url=${url},page=${totalPages - 1})}">
|
||||
<span aria-hidden="true">»</span>
|
||||
</a>
|
||||
</li>
|
||||
</th:block>
|
||||
</ul>
|
||||
</nav>
|
||||
</th:block>
|
||||
|
||||
</body>
|
||||
|
||||
</html>
|
47
src/main/resources/templates/product-edit.html
Normal file
47
src/main/resources/templates/product-edit.html
Normal file
@ -0,0 +1,47 @@
|
||||
<!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" class="w-50 mx-auto">
|
||||
<form action="#" th:action="@{/admin/product/edit/{id}(id=${product.id})}" th:object="${product}" method="post">
|
||||
<div class="mb-3">
|
||||
<label for="id" class="form-label">ID</label>
|
||||
<input type="text" th:value="*{id}" id="id" class="form-control" readonly disabled>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label for="name" class="form-label">Название</label>
|
||||
<input type="text" th:field="*{name}" id="name" class="form-control">
|
||||
<div th:if="${#fields.hasErrors('name')}" th:errors="*{name}" class="invalid-feedback"></div>
|
||||
</div>
|
||||
<div class="mb-2">
|
||||
<label for="typeId" class="form-label">Тип</label>
|
||||
<select th:field="*{typeId}" id="typeId" class="form-select">
|
||||
<option selected value="">Укажите тип</option>
|
||||
<option th:each="type : ${types}" th:value="${type.id}">[[${type.name}]]</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label for="price" class="form-label">Цена</label>
|
||||
<input type="number" th:field="*{price}" id="price" class="form-control">
|
||||
<div th:if="${#fields.hasErrors('price')}" th:errors="*{price}" class="invalid-feedback"></div>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label for="description" class="form-label">Описание</label>
|
||||
<textarea type="textarea" rows="5" cols="80" th:field="*{description}" id="description"
|
||||
class="form-control"></textarea>
|
||||
<div th:if="${#fields.hasErrors('description')}" th:errors="*{description}" class="invalid-feedback">
|
||||
</div>
|
||||
</div>
|
||||
<div class="mb-3 d-flex flex-row">
|
||||
<button class="btn btn-primary me-2 button-fixed-width" type="submit">Сохранить</button>
|
||||
<a class="btn btn-secondary button-fixed-width" href="/admin/product">Отмена</a>
|
||||
</div>
|
||||
</form>
|
||||
</main>
|
||||
</body>
|
||||
|
||||
</html>
|
55
src/main/resources/templates/product.html
Normal file
55
src/main/resources/templates/product.html
Normal file
@ -0,0 +1,55 @@
|
||||
<!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" class="w-50 mx-auto">
|
||||
<th:block th:switch="${items.size()}">
|
||||
<h2 th:case="0">Данные отсутствуют</h2>
|
||||
<th:block th:case="*">
|
||||
<h2 class="text-center">Меню</h2>
|
||||
<div>
|
||||
<a href="/admin/product/edit/" class="btn btn-primary">Добавить продукцию</a>
|
||||
</div>
|
||||
<table class="table">
|
||||
<caption></caption>
|
||||
<thead>
|
||||
<tr>
|
||||
<th scope="col" class="w-10">ID</th>
|
||||
<th scope="col" class="w-10">Название</th>
|
||||
<th scope="col" class="w-10">Цена</th>
|
||||
<th scope="col" class="w-auto">Описание</th>
|
||||
<th scope="col" class="w-10"></th>
|
||||
<th scope="col" class="w-10"></th>
|
||||
</tr>
|
||||
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr th:each="product : ${items}">
|
||||
<th scope="row" th:text="${product.id}"></th>
|
||||
<td th:text="${product.name}"></td>
|
||||
<td th:text="${product.price}"></td>
|
||||
<td th:text="${product.description}"></td>
|
||||
<td>
|
||||
<form th:action="@{/admin/product/edit/{id}(id=${product.id})}" method="get">
|
||||
<button type="submit" class="btn btn-link button-link">Редактировать</button>
|
||||
</form>
|
||||
</td>
|
||||
<td>
|
||||
<form th:action="@{/admin/product/delete/{id}(id=${product.id})}" method="post">
|
||||
<button type="submit" class="btn btn-link button-link"
|
||||
onclick="return confirm('Вы уверены?')">Удалить</button>
|
||||
</form>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</th:block>
|
||||
</th:block>
|
||||
</main>
|
||||
</body>
|
||||
|
||||
</html>
|
20
src/main/resources/templates/profile.html
Normal file
20
src/main/resources/templates/profile.html
Normal file
@ -0,0 +1,20 @@
|
||||
<!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" class="w-50 mx-auto main">
|
||||
<div class="tab-content mt-2">
|
||||
<h2 class="text-center">Заказы</h2>
|
||||
<div class="tab-pane container active" id="orders">
|
||||
<th:block
|
||||
th:replace="~{ orders :: orders (items=${items}, totalPages=${totalPages}, currentPage=${currentPage}, products=${products})}" />
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
</body>
|
||||
|
||||
</html>
|
@ -6,13 +6,18 @@
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<main layout:fragment="content">
|
||||
<main layout:fragment="content" class="w-25 mx-auto">
|
||||
<form action="#" th:action="@{/signup}" th:object="${user}" method="post">
|
||||
<div class="mb-3">
|
||||
<label for="login" class="form-label">Имя пользователя</label>
|
||||
<input type="text" th:field="*{login}" id="login" class="form-control">
|
||||
<div th:if="${#fields.hasErrors('login')}" th:errors="*{login}" class="invalid-feedback"></div>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label for="email" class="form-label">Почта</label>
|
||||
<input type="email" th:field="*{email}" id="email" class="form-control">
|
||||
<div th:if="${#fields.hasErrors('email')}" th:errors="*{email}" class="invalid-feedback"></div>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label for="password" class="form-label">Пароль</label>
|
||||
<input type="password" th:field="*{password}" id="password" class="form-control">
|
||||
@ -32,6 +37,4 @@
|
||||
</main>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
|
||||
</html>
|
33
src/main/resources/templates/top-products.html
Normal file
33
src/main/resources/templates/top-products.html
Normal file
@ -0,0 +1,33 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="ru" xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout" layout:decorate="~{default}">
|
||||
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>Top 5 Products</title>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<main layout:fragment="content" class="w-25 mx-auto">
|
||||
<h1>Top 5 Most Purchased Products</h1>
|
||||
<table class="table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>ID</th>
|
||||
<th>Name</th>
|
||||
<th>Description</th>
|
||||
<th>Price</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr th:each="product : ${topProducts}">
|
||||
<td th:text="${product.id}"></td>
|
||||
<td th:text="${product.name}"></td>
|
||||
<td th:text="${product.description}"></td>
|
||||
<td th:text="${product.price}"></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</main>
|
||||
</body>
|
||||
|
||||
</html>
|
@ -2,18 +2,18 @@
|
||||
<html lang="ru" xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout" layout:decorate="~{default}">
|
||||
|
||||
<head>
|
||||
<title>Редакторовать тип заказа</title>
|
||||
<title>Редакторовать тип продукции</title>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<main layout:fragment="content">
|
||||
<main layout:fragment="content" class="w-50 mx-auto">
|
||||
<form action="#" th:action="@{/admin/type/edit/{id}(id=${type.id})}" th:object="${type}" method="post">
|
||||
<div class="mb-3">
|
||||
<label for="id" class="form-label">ID</label>
|
||||
<input type="text" th:value="*{id}" id="id" class="form-control" readonly disabled>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label for="name" class="form-label">Тип заказа</label>
|
||||
<label for="name" class="form-label">Тип продукции</label>
|
||||
<input type="text" th:field="*{name}" id="name" class="form-control">
|
||||
<div th:if="${#fields.hasErrors('name')}" th:errors="*{name}" class="invalid-feedback"></div>
|
||||
</div>
|
||||
|
@ -2,24 +2,24 @@
|
||||
<html lang="ru" xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout" layout:decorate="~{default}">
|
||||
|
||||
<head>
|
||||
<title>Типы заказов</title>
|
||||
<title>Типы продукции</title>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<main layout:fragment="content">
|
||||
<main layout:fragment="content" class="w-50 mx-auto">
|
||||
<th:block th:switch="${items.size()}">
|
||||
<h2 th:case="0">Данные отсутствуют</h2>
|
||||
<th:block th:case="*">
|
||||
<h2>Типы заказов</h2>
|
||||
<h2 class="text-center">Типы продукции</h2>
|
||||
<div>
|
||||
<a href="/admin/type/edit/" class="btn btn-primary">Добавить тип заказа</a>
|
||||
<a href="/admin/type/edit/" class="btn btn-primary">Добавить тип продукции</a>
|
||||
</div>
|
||||
<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-10"></th>
|
||||
<th scope="col" class="w-10"></th>
|
||||
</tr>
|
||||
|
34
src/main/resources/templates/user-edit.html
Normal file
34
src/main/resources/templates/user-edit.html
Normal file
@ -0,0 +1,34 @@
|
||||
<!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" class="w-50 mx-auto">
|
||||
<form action="#" th:action="@{/admin/user/edit/{id}(id=${user.id},page=${page})}" th:object="${user}"
|
||||
method="post">
|
||||
<div class="mb-3">
|
||||
<label for="id" class="form-label">ID</label>
|
||||
<input type="text" th:value="*{id}" id="id" class="form-control" readonly disabled>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label for="login" class="form-label">Имя пользователя</label>
|
||||
<input type="text" th:field="*{login}" id="login" class="form-control">
|
||||
<div th:if="${#fields.hasErrors('login')}" th:errors="*{login}" class="invalid-feedback"></div>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label for="email" class="form-label">Почта</label>
|
||||
<input type="text" th:field="*{email}" id="email" class="form-control">
|
||||
<div th:if="${#fields.hasErrors('email')}" th:errors="*{email}" class="invalid-feedback"></div>
|
||||
</div>
|
||||
<div class="mb-3 d-flex flex-row">
|
||||
<button class="btn btn-primary me-2 button-fixed-width" type="submit">Сохранить</button>
|
||||
<a class="btn btn-secondary button-fixed-width" th:href="@{/admin/user(page=${page})}">Отмена</a>
|
||||
</div>
|
||||
</form>
|
||||
</main>
|
||||
</body>
|
||||
|
||||
</html>
|
58
src/main/resources/templates/user.html
Normal file
58
src/main/resources/templates/user.html
Normal file
@ -0,0 +1,58 @@
|
||||
<!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" class="w-50 mx-auto">
|
||||
<th:block th:switch="${items.size()}">
|
||||
<h2 th:case="0">Данные отсутствуют</h2>
|
||||
<th:block th:case="*">
|
||||
<h2 class=" text-center">Пользователи</h2>
|
||||
<div>
|
||||
<a th:href="@{/admin/user/edit/(page=${page})}" class="btn btn-primary">Добавить пользователя</a>
|
||||
</div>
|
||||
<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-10"></th>
|
||||
<th scope="col" class="w-10"></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr th:each="user : ${items}">
|
||||
<th scope="row" th:text="${user.id}"></th>
|
||||
<td th:text="${user.login}"></td>
|
||||
<td th:text="${user.email}"></td>
|
||||
<td>
|
||||
<form th:action="@{/admin/user/edit/{id}(id=${user.id})}" method="get">
|
||||
<input type="hidden" th:name="page" th:value="${page}">
|
||||
<button type="submit" class="btn btn-link button-link">Редактировать</button>
|
||||
</form>
|
||||
</td>
|
||||
<td>
|
||||
<form th:action="@{/admin/user/delete/{id}(id=${user.id})}" method="post">
|
||||
<input type="hidden" th:name="page" th:value="${page}">
|
||||
<button type="submit" class="btn btn-link button-link"
|
||||
onclick="return confirm('Вы уверены?')">Удалить</button>
|
||||
</form>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</th:block>
|
||||
<th:block th:replace="~{ pagination :: pagination (
|
||||
url=${'admin/user'},
|
||||
totalPages=${totalPages},
|
||||
currentPage=${currentPage}) }" />
|
||||
</th:block>
|
||||
</main>
|
||||
</body>
|
||||
|
||||
</html>
|
@ -1,98 +1,101 @@
|
||||
package com.example.demo;
|
||||
|
||||
import com.example.demo.core.error.NotFoundException;
|
||||
import com.example.demo.orders.model.OrderEntity;
|
||||
import com.example.demo.orders.service.OrderService;
|
||||
import com.example.demo.users.model.UserEntity;
|
||||
import com.example.demo.users.service.UserService;
|
||||
import com.example.demo.order_lines.model.OrderLineEntity;
|
||||
import com.example.demo.products.model.ProductEntity;
|
||||
import com.example.demo.products.service.ProductService;
|
||||
import com.example.demo.types.model.TypeEntity;
|
||||
import com.example.demo.types.service.TypeService;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import org.junit.jupiter.api.*;
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.Assertions;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Order;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.TestMethodOrder;
|
||||
import org.junit.jupiter.api.MethodOrderer.OrderAnnotation;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
|
||||
import java.util.List;
|
||||
import com.example.demo.core.error.NotFoundException;
|
||||
import com.example.demo.products.model.ProductEntity;
|
||||
import com.example.demo.products.service.ProductService;
|
||||
import com.example.demo.orders.model.OrderEntity;
|
||||
import com.example.demo.orders.service.OrderService;
|
||||
import com.example.demo.types.model.TypeEntity;
|
||||
import com.example.demo.types.service.TypeService;
|
||||
import com.example.demo.users.model.UserEntity;
|
||||
import com.example.demo.users.service.UserService;
|
||||
|
||||
@SpringBootTest
|
||||
class OrderServiceTests {
|
||||
@TestMethodOrder(OrderAnnotation.class)
|
||||
class OrderServiceTest {
|
||||
@Autowired
|
||||
private ProductService productService;
|
||||
@Autowired
|
||||
private TypeService typeService;
|
||||
@Autowired
|
||||
private UserService userService;
|
||||
@Autowired
|
||||
private OrderService orderService;
|
||||
|
||||
@Autowired
|
||||
private UserService userService;
|
||||
|
||||
@Autowired
|
||||
private ProductService productService;
|
||||
|
||||
@Autowired
|
||||
private TypeService typeService;
|
||||
|
||||
private OrderEntity order;
|
||||
private TypeEntity type1;
|
||||
private TypeEntity type2;
|
||||
private ProductEntity product1;
|
||||
private ProductEntity product2;
|
||||
private List<ProductEntity> products = new ArrayList<>();
|
||||
private UserEntity user1;
|
||||
private OrderEntity order1;
|
||||
|
||||
@BeforeEach
|
||||
void createData() {
|
||||
|
||||
type1 = typeService.create(new TypeEntity("Игра"));
|
||||
type2 = typeService.create(new TypeEntity("Программа"));
|
||||
|
||||
product1 = productService.create(new ProductEntity(type1, "Product1", 2100.0, "good product"));
|
||||
product2 = productService.create(new ProductEntity(type2, "Product2", 1200.0, "bad product"));
|
||||
products = new ArrayList<ProductEntity>();
|
||||
products.add(product1);
|
||||
products.add(product2);
|
||||
|
||||
user1 = userService.create(new UserEntity("login1", "email@mail.com", "qwerty123"));
|
||||
order1 = orderService.create(user1.getId(), new OrderEntity(products));
|
||||
removeData();
|
||||
// Создаем данные для тестирования
|
||||
TypeEntity type = typeService.create(new TypeEntity("musor"));
|
||||
UserEntity user = userService.create(new UserEntity("John", "Doe", "privet@gmail.ru", "passwd"));
|
||||
ProductEntity product = productService.create(new ProductEntity("Laptop", type, 400.00));
|
||||
order = new OrderEntity(user);
|
||||
OrderLineEntity line = new OrderLineEntity(product, 2);
|
||||
order.addOrderLine(line);
|
||||
orderService.create(order);
|
||||
|
||||
type1 = typeService.create(new TypeEntity("Игра"));
|
||||
type2 = typeService.create(new TypeEntity("Программа"));
|
||||
|
||||
product1 = productService.create(new ProductEntity(type1, "Product1", 2100.0, "good product"));
|
||||
product2 = productService.create(new ProductEntity(type2, "Product2", 1200.0, "bad product"));
|
||||
products = new ArrayList<ProductEntity>();
|
||||
products.add(product1);
|
||||
products.add(product2);
|
||||
|
||||
user1 = userService.create(new UserEntity("login1", "email@mail.com", "qwerty123"));
|
||||
order1 = orderService.create(user1.getId(), new OrderEntity(products));
|
||||
orderService.create(user1.getId(), new OrderEntity(products));
|
||||
}
|
||||
|
||||
@AfterEach
|
||||
void removeData() {
|
||||
// Удаляем все данные после каждого теста
|
||||
orderService.delete(order.getId());
|
||||
userService.delete(order.getUser().getId());
|
||||
productService.delete(order.getLines().get(0).getProduct().getId());
|
||||
typeService.delete(order.getLines().get(0).getProduct().getType().getId());
|
||||
}
|
||||
|
||||
@Test
|
||||
void getAllTest() {
|
||||
List<OrderEntity> orders = orderService.getAll(0L, List.of());
|
||||
Assertions.assertNotNull(orders);
|
||||
Assertions.assertFalse(orders.isEmpty());
|
||||
orderService.getAll().forEach(item -> orderService.delete(item.getUser().getId(), item.getId()));
|
||||
userService.getAll().forEach(item -> userService.delete(item.getId()));
|
||||
productService.getAll(0).forEach(item -> productService.delete(item.getId()));
|
||||
typeService.getAll().forEach(item -> typeService.delete(item.getId()));
|
||||
}
|
||||
|
||||
@Test
|
||||
void getTest() {
|
||||
// Тестируем, что найденный заказ совпадает с ожидаемым
|
||||
OrderEntity foundOrder = orderService.get(order.getId());
|
||||
Assertions.assertNotNull(foundOrder);
|
||||
Assertions.assertEquals(order, foundOrder);
|
||||
|
||||
// Проверяем, что вызов с несуществующим id бросает исключение
|
||||
Assertions.assertThrows(NotFoundException.class, () -> orderService.get(0L));
|
||||
Assertions.assertThrows(NotFoundException.class, () -> productService.get(0L));
|
||||
}
|
||||
|
||||
@Test
|
||||
@Order(1)
|
||||
void createTest() {
|
||||
// Проверяем, что созданный заказ совпадает с ожидаемым
|
||||
Assertions.assertNotNull(order.getId());
|
||||
OrderEntity foundOrder = orderService.get(order.getId());
|
||||
Assertions.assertEquals(order, foundOrder);
|
||||
}
|
||||
|
||||
@Test
|
||||
void updateTest() {
|
||||
// Обновляем заказ и проверяем, что он изменился
|
||||
OrderEntity updatedOrder = orderService.update(order.getId(), order);
|
||||
Assertions.assertNotNull(updatedOrder);
|
||||
Assertions.assertEquals(order, updatedOrder);
|
||||
Assertions.assertEquals(2, orderService.getAll(user1.getId()).size());
|
||||
}
|
||||
|
||||
@Test
|
||||
@Order(2)
|
||||
void deleteTest() {
|
||||
// Удаляем заказ и проверяем, что его нет в базе данных
|
||||
orderService.delete(order.getId());
|
||||
Assertions.assertThrows(NotFoundException.class, () -> orderService.get(order.getId()));
|
||||
orderService.delete(user1.getId(), order1.getId());
|
||||
Assertions.assertEquals(1, orderService.getAll(user1.getId()).size());
|
||||
}
|
||||
}
|
||||
}
|
@ -1,24 +1,57 @@
|
||||
package com.example.demo;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.Assertions;
|
||||
import org.junit.jupiter.api.MethodOrderer.OrderAnnotation;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Order;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.TestMethodOrder;
|
||||
import org.junit.jupiter.api.MethodOrderer.OrderAnnotation;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
|
||||
import com.example.demo.core.error.NotFoundException;
|
||||
import com.example.demo.products.api.ProductDto;
|
||||
import com.example.demo.products.model.ProductEntity;
|
||||
import com.example.demo.products.service.ProductService;
|
||||
import com.example.demo.types.model.TypeEntity;
|
||||
import com.example.demo.types.service.TypeService;
|
||||
|
||||
@SpringBootTest
|
||||
@TestMethodOrder(OrderAnnotation.class)
|
||||
class ProductServiceTests {
|
||||
|
||||
@Autowired
|
||||
private ProductService productService;
|
||||
|
||||
@Autowired
|
||||
private TypeService typeService;
|
||||
|
||||
private TypeEntity type1;
|
||||
private TypeEntity type2;
|
||||
|
||||
private ProductEntity product1;
|
||||
private ProductEntity product2;
|
||||
|
||||
@BeforeEach
|
||||
void createData() {
|
||||
removeData();
|
||||
|
||||
type1 = typeService.create(new TypeEntity("Напиток"));
|
||||
type2 = typeService.create(new TypeEntity("Пицца"));
|
||||
|
||||
product1 = productService.create(new ProductEntity(type1, "Product1", 2100.0, "good product"));
|
||||
product2 = productService.create(new ProductEntity(type2, "Product2", 1200.0, "bad product"));
|
||||
}
|
||||
|
||||
@AfterEach
|
||||
void removeData() {
|
||||
productService.getAll(0).forEach(item -> productService.delete(item.getId()));
|
||||
typeService.getAll().forEach(item -> typeService.delete(item.getId()));
|
||||
}
|
||||
|
||||
@Test
|
||||
void getTest() {
|
||||
Assertions.assertThrows(NotFoundException.class, () -> productService.get(0L));
|
||||
@ -27,31 +60,34 @@ class ProductServiceTests {
|
||||
@Test
|
||||
@Order(1)
|
||||
void createTest() {
|
||||
|
||||
final var type1 = new TypeEntity("Пицца");
|
||||
productService.create(new ProductEntity("Mocarela", type1, 20.00));
|
||||
productService.create(new ProductEntity("El'Diablo", type1, 20.00));
|
||||
productService.create(new ProductEntity("Маргарита", type1, 499.00));
|
||||
productService.create(new ProductEntity("Эль Дьябло", type1, 699.00));
|
||||
productService.create(new ProductEntity("Гавайская", type1, 399.00));
|
||||
Assertions.assertEquals(5, productService.getAll(0L).size());
|
||||
Assertions.assertEquals(2, productService.getAll(0).size());
|
||||
}
|
||||
|
||||
@Test
|
||||
@Order(2)
|
||||
void updateTest() {
|
||||
final ProductEntity newProduct = new ProductEntity("El'Diablo", new TypeEntity("Пицца"), 20.00);
|
||||
final ProductEntity updProduct = productService.update(1L, newProduct);
|
||||
Assertions.assertEquals(5, productService.getAll(0L).size());
|
||||
Assertions.assertEquals(updProduct, productService.get(1L));
|
||||
Assertions.assertEquals(newProduct.getName(), updProduct.getName());
|
||||
Assertions.assertEquals(newProduct.getPrice(), updProduct.getPrice());
|
||||
TypeEntity newType = typeService.create(new TypeEntity("Закуска"));
|
||||
ProductEntity updatedProduct = new ProductEntity(newType, "testProduct", 1200.0, "hehproduct");
|
||||
|
||||
productService.update(product1.getId(), updatedProduct);
|
||||
|
||||
List<ProductEntity> allProducts = productService.getAll(0);
|
||||
Assertions.assertEquals(2, allProducts.size());
|
||||
|
||||
ProductEntity retrievedProduct = productService.get(product1.getId());
|
||||
Assertions.assertEquals("testProduct", retrievedProduct.getName());
|
||||
Assertions.assertEquals(1200.0, retrievedProduct.getPrice());
|
||||
Assertions.assertEquals("hehproduct", retrievedProduct.getDescription());
|
||||
Assertions.assertEquals(newType.getId(), retrievedProduct.getType().getId());
|
||||
}
|
||||
|
||||
@Test
|
||||
@Order(3)
|
||||
void deleteTest() {
|
||||
productService.delete(2L);
|
||||
Assertions.assertEquals(4L, productService.getAll(0L).size());
|
||||
productService.delete(product1.getId());
|
||||
Assertions.assertEquals(1, productService.getAll(0).size());
|
||||
|
||||
ProductEntity remainingProduct = productService.get(product2.getId());
|
||||
Assertions.assertEquals(product2.getId(), remainingProduct.getId());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,10 +1,12 @@
|
||||
package com.example.demo;
|
||||
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.Assertions;
|
||||
import org.junit.jupiter.api.MethodOrderer.OrderAnnotation;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Order;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.TestMethodOrder;
|
||||
import org.junit.jupiter.api.MethodOrderer.OrderAnnotation;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
|
||||
@ -12,50 +14,61 @@ import com.example.demo.core.error.NotFoundException;
|
||||
import com.example.demo.types.model.TypeEntity;
|
||||
import com.example.demo.types.service.TypeService;
|
||||
|
||||
import jakarta.transaction.Transactional;
|
||||
|
||||
@SpringBootTest
|
||||
@TestMethodOrder(OrderAnnotation.class)
|
||||
class TypeServiceTests {
|
||||
@Autowired
|
||||
private TypeService typeService;
|
||||
|
||||
private TypeEntity type1;
|
||||
private TypeEntity type2;
|
||||
|
||||
@BeforeEach
|
||||
void createData() {
|
||||
removeData();
|
||||
type2 = typeService.create(new TypeEntity("Программа"));
|
||||
type1 = typeService.create(new TypeEntity("Игра"));
|
||||
}
|
||||
|
||||
@AfterEach
|
||||
void removeData() {
|
||||
typeService.getAll().forEach(item -> typeService.delete(item.getId()));
|
||||
}
|
||||
|
||||
@Test
|
||||
@Transactional
|
||||
void getTest() {
|
||||
Assertions.assertThrows(NotFoundException.class, () -> typeService.get(0L));
|
||||
}
|
||||
|
||||
@Test
|
||||
@Order(1)
|
||||
@Transactional
|
||||
void createTest() {
|
||||
typeService.create(new TypeEntity("Пицца"));
|
||||
typeService.create(new TypeEntity("Напиток"));
|
||||
final TypeEntity last = typeService.create(new TypeEntity("Закуска"));
|
||||
final TypeEntity last = typeService.create(new TypeEntity("Игра2"));
|
||||
Assertions.assertEquals(3, typeService.getAll().size());
|
||||
Assertions.assertEquals(last, typeService.get(3L));
|
||||
Assertions.assertEquals(last, typeService.get(last.getId()));
|
||||
}
|
||||
|
||||
@Test
|
||||
@Order(2)
|
||||
@Transactional
|
||||
void updateTest() {
|
||||
final String test = "TEST";
|
||||
final TypeEntity entity = typeService.get(3L);
|
||||
final String oldName = entity.getName();
|
||||
final TypeEntity newEntity = typeService.update(3L, new TypeEntity(test));
|
||||
Assertions.assertEquals(3, typeService.getAll().size());
|
||||
Assertions.assertEquals(newEntity, typeService.get(3L));
|
||||
final TypeEntity newEntity = typeService.update(type1.getId(), new TypeEntity(test));
|
||||
Assertions.assertEquals(2, typeService.getAll().size());
|
||||
Assertions.assertEquals(test, newEntity.getName());
|
||||
Assertions.assertNotEquals(oldName, newEntity.getName());
|
||||
}
|
||||
|
||||
@Test
|
||||
@Order(3)
|
||||
@Transactional
|
||||
void deleteTest() {
|
||||
typeService.delete(3L);
|
||||
Assertions.assertEquals(2, typeService.getAll().size());
|
||||
final TypeEntity last = typeService.get(2L);
|
||||
Assertions.assertEquals(2L, last.getId());
|
||||
|
||||
final TypeEntity newEntity = typeService.create(new TypeEntity("Закуска"));
|
||||
Assertions.assertEquals(3, typeService.getAll().size());
|
||||
Assertions.assertEquals(4L, newEntity.getId());
|
||||
typeService.delete(type1.getId());
|
||||
Assertions.assertEquals(1, typeService.getAll().size());
|
||||
final TypeEntity last = typeService.get(type2.getId());
|
||||
Assertions.assertEquals(type2.getId(), last.getId());
|
||||
}
|
||||
}
|
||||
|
@ -3,10 +3,10 @@ package com.example.demo;
|
||||
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.junit.jupiter.api.MethodOrderer.OrderAnnotation;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
|
||||
@ -20,21 +20,20 @@ class UserServiceTests {
|
||||
@Autowired
|
||||
private UserService userService;
|
||||
|
||||
private UserEntity user1;
|
||||
private UserEntity user2;
|
||||
|
||||
@BeforeEach
|
||||
void createData() {
|
||||
UserEntity user1 = new UserEntity("John", "Doe", "gt434ge@fkgjfdj.com", "password");
|
||||
UserEntity user2 = new UserEntity("Alex", "Kryukov", "fhegehr@ghsjg.com", "password");
|
||||
UserEntity user3 = new UserEntity("Alex", "Kryukov", "fhegeуйцуйцhr@ghsjg.com", "password");
|
||||
userService.create(user1);
|
||||
userService.create(user2);
|
||||
userService.create(user3);
|
||||
removeData();
|
||||
|
||||
user1 = userService.create(new UserEntity("login1", "email@mail.com", "qwerty123"));
|
||||
user2 = userService.create(new UserEntity("login2", "email@gmail.com", "qwerty1234"));
|
||||
}
|
||||
|
||||
@AfterEach
|
||||
void clearData() {
|
||||
userService.delete(1L);
|
||||
userService.delete(2L);
|
||||
userService.delete(3L);
|
||||
void removeData() {
|
||||
userService.getAll().forEach(item -> userService.delete(item.getId()));
|
||||
}
|
||||
|
||||
@Test
|
||||
@ -45,34 +44,23 @@ class UserServiceTests {
|
||||
@Test
|
||||
@Order(1)
|
||||
void createTest() {
|
||||
final UserEntity last = userService
|
||||
.create(new UserEntity("Alex", "selivanov", "fheg123ehr@ghsjg.com", "password"));
|
||||
Assertions.assertEquals(4, userService.getAll().size());
|
||||
Assertions.assertEquals(last, userService.get(4L));
|
||||
Assertions.assertEquals(2, userService.getAll().size());
|
||||
}
|
||||
|
||||
@Test
|
||||
@Order(2)
|
||||
void updateTest() {
|
||||
final UserEntity entity = userService.get(3L);
|
||||
final String oldName = entity.getFullname();
|
||||
final UserEntity newEntity = userService.update(3L, new UserEntity("test", "test", "test", "test"));
|
||||
Assertions.assertEquals(3, userService.getAll().size());
|
||||
Assertions.assertEquals(newEntity, userService.get(3L));
|
||||
Assertions.assertEquals("test", newEntity.getFullname());
|
||||
Assertions.assertNotEquals(oldName, newEntity.getFullname());
|
||||
final UserEntity newEntity = userService.update(user2.getId(), new UserEntity("user11", "mail11", "qwerty11"));
|
||||
Assertions.assertEquals(2, userService.getAll().size());
|
||||
Assertions.assertEquals(newEntity.getLogin(), "user11");
|
||||
}
|
||||
|
||||
@Test
|
||||
@Order(3)
|
||||
void deleteTest() {
|
||||
userService.delete(3L);
|
||||
Assertions.assertEquals(2, userService.getAll().size());
|
||||
final UserEntity last = userService.get(2L);
|
||||
Assertions.assertEquals(2L, last.getId());
|
||||
|
||||
final UserEntity newEntity = userService.create(new UserEntity());
|
||||
Assertions.assertEquals(3, userService.getAll().size());
|
||||
Assertions.assertEquals(4L, newEntity.getId());
|
||||
userService.delete(user2.getId());
|
||||
Assertions.assertEquals(1, userService.getAll().size());
|
||||
final UserEntity last = userService.get(user1.getId());
|
||||
Assertions.assertEquals(user1.getId(), last.getId());
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user