что-то мутится
This commit is contained in:
parent
d47f50ba29
commit
e59d4c9465
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.
Binary file not shown.
Binary file not shown.
Binary file not shown.
22
build.gradle
22
build.gradle
@ -1,12 +1,23 @@
|
|||||||
plugins {
|
plugins {
|
||||||
id 'java'
|
id 'java'
|
||||||
id 'org.springframework.boot' version '3.2.3'
|
id 'org.springframework.boot' version '3.2.4'
|
||||||
id 'io.spring.dependency-management' version '1.1.4'
|
id 'io.spring.dependency-management' version '1.1.4'
|
||||||
}
|
}
|
||||||
|
|
||||||
group = 'com.example'
|
group = 'com.example'
|
||||||
version = '0.0.1-SNAPSHOT'
|
version = '0.0.1-SNAPSHOT'
|
||||||
|
|
||||||
|
defaultTasks 'bootRun'
|
||||||
|
|
||||||
|
jar {
|
||||||
|
enabled = false
|
||||||
|
}
|
||||||
|
|
||||||
|
bootJar {
|
||||||
|
archiveFileName = String.format('%s-%s.jar', rootProject.name, version)
|
||||||
|
}
|
||||||
|
|
||||||
|
assert System.properties['java.specification.version'] == '17' || '21'
|
||||||
java {
|
java {
|
||||||
sourceCompatibility = '17'
|
sourceCompatibility = '17'
|
||||||
}
|
}
|
||||||
@ -18,12 +29,19 @@ repositories {
|
|||||||
dependencies {
|
dependencies {
|
||||||
implementation 'org.springframework.boot:spring-boot-starter-web'
|
implementation 'org.springframework.boot:spring-boot-starter-web'
|
||||||
implementation 'org.springframework.boot:spring-boot-starter-validation'
|
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.modelmapper:modelmapper:3.2.0'
|
||||||
|
|
||||||
implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
|
implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
|
||||||
implementation 'com.h2database:h2:2.2.224'
|
implementation 'com.h2database:h2:2.2.224'
|
||||||
|
|
||||||
|
implementation 'org.springframework.boot:spring-boot-devtools'
|
||||||
|
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'
|
||||||
|
|
||||||
|
implementation 'org.springframework.boot:spring-boot-starter-security'
|
||||||
|
implementation 'org.thymeleaf.extras:thymeleaf-extras-springsecurity6'
|
||||||
|
|
||||||
testImplementation 'org.springframework.boot:spring-boot-starter-test'
|
testImplementation 'org.springframework.boot:spring-boot-starter-test'
|
||||||
}
|
}
|
||||||
|
67
build/resources/main/public/css/style.css
Normal file
67
build/resources/main/public/css/style.css
Normal file
@ -0,0 +1,67 @@
|
|||||||
|
html,
|
||||||
|
body {
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
h1 {
|
||||||
|
font-size: 1.5em;
|
||||||
|
}
|
||||||
|
|
||||||
|
h2 {
|
||||||
|
font-size: 1.25em;
|
||||||
|
}
|
||||||
|
|
||||||
|
h3 {
|
||||||
|
font-size: 1.1em;
|
||||||
|
}
|
||||||
|
|
||||||
|
td form {
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
margin-top: -.25em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.button-fixed-width {
|
||||||
|
width: 150px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.button-link {
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.invalid-feedback {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
.w-10 {
|
||||||
|
width: 10% !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.my-navbar {
|
||||||
|
background-color: #3c3c3c !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.my-navbar .link a:hover {
|
||||||
|
text-decoration: underline;
|
||||||
|
}
|
||||||
|
|
||||||
|
.my-navbar .logo {
|
||||||
|
width: 26px;
|
||||||
|
height: 26px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.my-footer {
|
||||||
|
background-color: #2c2c2c;
|
||||||
|
height: 32px;
|
||||||
|
color: rgba(255, 255, 255, 0.5);
|
||||||
|
}
|
||||||
|
|
||||||
|
.cart-image {
|
||||||
|
width: 3.1rem;
|
||||||
|
padding: 0.25rem;
|
||||||
|
border-radius: 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cart-item {
|
||||||
|
height: auto;
|
||||||
|
}
|
37
build/resources/main/static/error.html
Normal file
37
build/resources/main/static/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>
|
71
build/resources/main/templates/default.html
Normal file
71
build/resources/main/templates/default.html
Normal file
@ -0,0 +1,71 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="ru" data-bs-theme="dark" xmlns:th="http://www.thymeleaf.org"
|
||||||
|
xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout"
|
||||||
|
xmlns:sec="http://www.thymeleaf.org/thymeleaf-extras-springsecurity6">
|
||||||
|
|
||||||
|
<head>
|
||||||
|
<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>
|
||||||
|
<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" />
|
||||||
|
<link rel="stylesheet" href="/css/style.css" />
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body class="h-100 d-flex flex-column">
|
||||||
|
<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>
|
||||||
|
Туда сюда и пицца
|
||||||
|
</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"
|
||||||
|
aria-controls="main-navbar" aria-expanded="false" aria-label="Toggle navigation">
|
||||||
|
<span class="navbar-toggler-icon"></span>
|
||||||
|
</button>
|
||||||
|
<div class="collapse navbar-collapse" id="main-navbar">
|
||||||
|
<ul class="navbar-nav me-auto link" th:with="activeLink=${#objects.nullSafe(servletPath, '')}">
|
||||||
|
<th:block sec:authorize="hasRole('ADMIN')">
|
||||||
|
<a class="nav-link" href="/admin/user"
|
||||||
|
th:classappend="${activeLink.startsWith('/admin/user') ? 'active' : ''}">
|
||||||
|
Пользователи
|
||||||
|
</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>
|
||||||
|
<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>
|
||||||
|
</ul>
|
||||||
|
<ul class="navbar-nav" th:if="${not #strings.isEmpty(userName)}">
|
||||||
|
<form th:action="@{/logout}" method="post">
|
||||||
|
<button type="submit" class="navbar-brand nav-link" onclick="return confirm('Вы уверены?')">
|
||||||
|
Выход ([[${userName}]])
|
||||||
|
</button>
|
||||||
|
</form>
|
||||||
|
<a class="navbar-brand" href="/cart">
|
||||||
|
<i class="bi bi-cart2 d-inline-block align-top me-1 logo"></i>
|
||||||
|
[[${#numbers.formatDecimal(totalCart, 1, 2)}]] ₽
|
||||||
|
</a>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</th:block>
|
||||||
|
</div>
|
||||||
|
</nav>
|
||||||
|
<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())}]]
|
||||||
|
</footer>
|
||||||
|
</body>
|
||||||
|
|
||||||
|
</html>
|
44
build/resources/main/templates/login.html
Normal file
44
build/resources/main/templates/login.html
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
<!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">
|
||||||
|
<form action="#" th:action="@{/login}" method="post">
|
||||||
|
<div th:if="${param.error}" class="alert alert-danger">
|
||||||
|
Неверный логин или пароль
|
||||||
|
</div>
|
||||||
|
<div th:if="${param.logout}" class="alert alert-success">
|
||||||
|
Выход успешно произведен
|
||||||
|
</div>
|
||||||
|
<div th:if="${param.signup}" class="alert alert-success">
|
||||||
|
Пользователь успешно создан
|
||||||
|
</div>
|
||||||
|
<div class="mb-3">
|
||||||
|
<label for="username" class="form-label">Имя пользователя</label>
|
||||||
|
<input type="text" id="username" name="username" class="form-control" required minlength="3"
|
||||||
|
maxlength="20">
|
||||||
|
</div>
|
||||||
|
<div class="mb-3">
|
||||||
|
<label for="password" class="form-label">Пароль</label>
|
||||||
|
<input type="password" id="password" name="password" class="form-control" required minlength="3"
|
||||||
|
maxlength="20">
|
||||||
|
</div>
|
||||||
|
<div class="form-check mb-3">
|
||||||
|
<input class="form-check-input" type="checkbox" id="remember-me" name="remember-me" checked>
|
||||||
|
<label class="form-check-label" for="remember-me">Запомнить меня</label>
|
||||||
|
</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="/signup">Регистрация</a>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</main>
|
||||||
|
</body>
|
||||||
|
|
||||||
|
</html>
|
||||||
|
|
||||||
|
</html>
|
37
build/resources/main/templates/signup.html
Normal file
37
build/resources/main/templates/signup.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">
|
||||||
|
<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="password" class="form-label">Пароль</label>
|
||||||
|
<input type="password" th:field="*{password}" id="password" class="form-control">
|
||||||
|
<div th:if="${#fields.hasErrors('password')}" th:errors="*{password}" class="invalid-feedback"></div>
|
||||||
|
</div>
|
||||||
|
<div class="mb-3">
|
||||||
|
<label for="passwordConfirm" class="form-label">Пароль (подтверждение)</label>
|
||||||
|
<input type="password" th:field="*{passwordConfirm}" id="passwordConfirm" class="form-control">
|
||||||
|
<div th:if="${#fields.hasErrors('passwordConfirm')}" th:errors="*{passwordConfirm}"
|
||||||
|
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="/">Отмена</a>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</main>
|
||||||
|
</body>
|
||||||
|
|
||||||
|
</html>
|
||||||
|
|
||||||
|
</html>
|
28
build/resources/main/templates/type-edit.html
Normal file
28
build/resources/main/templates/type-edit.html
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
<!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">
|
||||||
|
<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>
|
||||||
|
<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-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/type">Отмена</a>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</main>
|
||||||
|
</body>
|
||||||
|
|
||||||
|
</html>
|
50
build/resources/main/templates/type.html
Normal file
50
build/resources/main/templates/type.html
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="ru" xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout" layout:decorate="~{default}">
|
||||||
|
|
||||||
|
<head>
|
||||||
|
<title>Типы заказов</title>
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
<main layout:fragment="content">
|
||||||
|
<th:block th:switch="${items.size()}">
|
||||||
|
<h2 th:case="0">Данные отсутствуют</h2>
|
||||||
|
<th:block th:case="*">
|
||||||
|
<h2>Типы заказов</h2>
|
||||||
|
<div>
|
||||||
|
<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-10"></th>
|
||||||
|
<th scope="col" class="w-10"></th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<tr th:each="type : ${items}">
|
||||||
|
<th scope="row" th:text="${type.id}"></th>
|
||||||
|
<td th:text="${type.name}"></td>
|
||||||
|
<td>
|
||||||
|
<form th:action="@{/admin/type/edit/{id}(id=${type.id})}" method="get">
|
||||||
|
<button type="submit" class="btn btn-link button-link">Редактировать</button>
|
||||||
|
</form>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<form th:action="@{/admin/type/delete/{id}(id=${type.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>
|
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.
Binary file not shown.
Binary file not shown.
BIN
data.mv.db
BIN
data.mv.db
Binary file not shown.
59827
data.trace.db
Normal file
59827
data.trace.db
Normal file
File diff suppressed because it is too large
Load Diff
@ -1,7 +1,5 @@
|
|||||||
package com.example.demo;
|
package com.example.demo;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
|
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
@ -10,9 +8,6 @@ import org.springframework.boot.CommandLineRunner;
|
|||||||
import org.springframework.boot.SpringApplication;
|
import org.springframework.boot.SpringApplication;
|
||||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||||
|
|
||||||
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.model.ProductEntity;
|
||||||
import com.example.demo.products.service.ProductService;
|
import com.example.demo.products.service.ProductService;
|
||||||
import com.example.demo.types.model.TypeEntity;
|
import com.example.demo.types.model.TypeEntity;
|
||||||
@ -27,14 +22,11 @@ public class DemoApplication implements CommandLineRunner {
|
|||||||
private final TypeService typeService;
|
private final TypeService typeService;
|
||||||
private final ProductService productService;
|
private final ProductService productService;
|
||||||
private final UserService userService;
|
private final UserService userService;
|
||||||
private final OrderService orderService;
|
|
||||||
|
|
||||||
public DemoApplication(TypeService typeService, ProductService productService, UserService userService,
|
public DemoApplication(TypeService typeService, ProductService productService, UserService userService) {
|
||||||
OrderService orderService) {
|
|
||||||
this.typeService = typeService;
|
this.typeService = typeService;
|
||||||
this.productService = productService;
|
this.productService = productService;
|
||||||
this.userService = userService;
|
this.userService = userService;
|
||||||
this.orderService = orderService;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void main(String[] args) {
|
public static void main(String[] args) {
|
||||||
@ -62,13 +54,6 @@ public class DemoApplication implements CommandLineRunner {
|
|||||||
log.info("Create default users values");
|
log.info("Create default users values");
|
||||||
userService.create(new UserEntity("Alex", "Kryukov", "akryu@mail.ru", "password123"));
|
userService.create(new UserEntity("Alex", "Kryukov", "akryu@mail.ru", "password123"));
|
||||||
userService.create(new UserEntity("Oleg", "Zyngin", "@mail.ru", "password"));
|
userService.create(new UserEntity("Oleg", "Zyngin", "@mail.ru", "password"));
|
||||||
|
|
||||||
log.info("Create default orders values");
|
|
||||||
@SuppressWarnings({ "rawtypes", "unchecked" })
|
|
||||||
List<OrderLineEntity> lines = new ArrayList();
|
|
||||||
lines.add(new OrderLineEntity(null, 3));
|
|
||||||
final var user1 = userService.create(new UserEntity("Misha", "Kryukov", "akryu132@mail.ru", "password"));
|
|
||||||
orderService.create(new OrderEntity(user1));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,14 +1,13 @@
|
|||||||
package com.example.demo.order_lines.model;
|
package com.example.demo.order_lines.model;
|
||||||
|
|
||||||
import java.util.Objects;
|
|
||||||
|
|
||||||
import jakarta.persistence.Column;
|
import jakarta.persistence.Column;
|
||||||
import jakarta.persistence.Entity;
|
import jakarta.persistence.Entity;
|
||||||
import jakarta.persistence.JoinColumn;
|
import jakarta.persistence.JoinColumn;
|
||||||
import jakarta.persistence.ManyToOne;
|
import jakarta.persistence.ManyToOne;
|
||||||
import jakarta.persistence.OneToMany;
|
|
||||||
import jakarta.persistence.Table;
|
import jakarta.persistence.Table;
|
||||||
|
|
||||||
|
import java.util.Objects;
|
||||||
|
|
||||||
import com.example.demo.core.model.BaseEntity;
|
import com.example.demo.core.model.BaseEntity;
|
||||||
import com.example.demo.products.model.ProductEntity;
|
import com.example.demo.products.model.ProductEntity;
|
||||||
|
|
||||||
@ -17,19 +16,22 @@ import com.example.demo.products.model.ProductEntity;
|
|||||||
public class OrderLineEntity extends BaseEntity {
|
public class OrderLineEntity extends BaseEntity {
|
||||||
@Column(nullable = false)
|
@Column(nullable = false)
|
||||||
private Integer count;
|
private Integer count;
|
||||||
|
|
||||||
@ManyToOne
|
@ManyToOne
|
||||||
@JoinColumn(name = "productId", nullable = false)
|
@JoinColumn(name = "productId", nullable = false)
|
||||||
private ProductEntity product;
|
private ProductEntity product;
|
||||||
|
|
||||||
@Column(nullable = false)
|
@Column(nullable = false)
|
||||||
private Double totalPrice;
|
private Double totalPrice;
|
||||||
|
|
||||||
public OrderLineEntity() {
|
public OrderLineEntity() {
|
||||||
|
// Конструктор
|
||||||
}
|
}
|
||||||
|
|
||||||
public OrderLineEntity(ProductEntity product, Integer count) {
|
public OrderLineEntity(ProductEntity product, Integer count) {
|
||||||
this.product = product;
|
this.product = product;
|
||||||
this.count = count;
|
this.count = count;
|
||||||
|
calculateTotalPrice(); // Рассчитываем сумму при создании
|
||||||
}
|
}
|
||||||
|
|
||||||
public ProductEntity getProduct() {
|
public ProductEntity getProduct() {
|
||||||
@ -38,6 +40,7 @@ public class OrderLineEntity extends BaseEntity {
|
|||||||
|
|
||||||
public void setProduct(ProductEntity product) {
|
public void setProduct(ProductEntity product) {
|
||||||
this.product = product;
|
this.product = product;
|
||||||
|
calculateTotalPrice(); // Пересчитываем сумму при установке продукта
|
||||||
}
|
}
|
||||||
|
|
||||||
public Integer getCount() {
|
public Integer getCount() {
|
||||||
@ -46,6 +49,7 @@ public class OrderLineEntity extends BaseEntity {
|
|||||||
|
|
||||||
public void setCount(Integer count) {
|
public void setCount(Integer count) {
|
||||||
this.count = count;
|
this.count = count;
|
||||||
|
calculateTotalPrice(); // Пересчитываем сумму при установке количества
|
||||||
}
|
}
|
||||||
|
|
||||||
public Double getTotalPrice() {
|
public Double getTotalPrice() {
|
||||||
@ -56,6 +60,14 @@ public class OrderLineEntity extends BaseEntity {
|
|||||||
this.totalPrice = totalPrice;
|
this.totalPrice = totalPrice;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void calculateTotalPrice() {
|
||||||
|
if (product != null && product.getPrice() != null && count != null) {
|
||||||
|
totalPrice = product.getPrice() * count;
|
||||||
|
} else {
|
||||||
|
totalPrice = 0.0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int hashCode() {
|
public int hashCode() {
|
||||||
return Objects.hash(id, product, count, totalPrice);
|
return Objects.hash(id, product, count, totalPrice);
|
||||||
|
@ -2,6 +2,8 @@ package com.example.demo.orders.api;
|
|||||||
|
|
||||||
import java.sql.Date;
|
import java.sql.Date;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
import org.modelmapper.ModelMapper;
|
import org.modelmapper.ModelMapper;
|
||||||
import org.springframework.web.bind.annotation.*;
|
import org.springframework.web.bind.annotation.*;
|
||||||
import com.example.demo.core.configuration.Constants;
|
import com.example.demo.core.configuration.Constants;
|
||||||
@ -11,6 +13,7 @@ import com.example.demo.orders.model.OrderEntity;
|
|||||||
import com.example.demo.orders.service.OrderService;
|
import com.example.demo.orders.service.OrderService;
|
||||||
import com.example.demo.products.model.ProductEntity;
|
import com.example.demo.products.model.ProductEntity;
|
||||||
import com.example.demo.products.service.ProductService;
|
import com.example.demo.products.service.ProductService;
|
||||||
|
import com.example.demo.users.model.UserEntity;
|
||||||
import com.example.demo.users.service.UserService;
|
import com.example.demo.users.service.UserService;
|
||||||
import jakarta.validation.Valid;
|
import jakarta.validation.Valid;
|
||||||
|
|
||||||
@ -48,19 +51,26 @@ public class OrderController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public OrderEntity toEntity(OrderDto dto) {
|
public OrderEntity toEntity(OrderDto dto) {
|
||||||
final OrderEntity entity = modelMapper.map(dto, OrderEntity.class);
|
OrderEntity entity = modelMapper.map(dto, OrderEntity.class);
|
||||||
if (dto.getUserId() != null) {
|
UserEntity user = userService.get(dto.getUserId());
|
||||||
entity.setUser(userService.get(dto.getUserId()));
|
entity.setUser(user);
|
||||||
}
|
|
||||||
entity.getLines().clear();
|
List<OrderLineEntity> orderLines = dto.getLines().stream()
|
||||||
for (OrderLineDto lineDto : dto.getLines()) {
|
.map(lineDto -> {
|
||||||
OrderLineEntity orderLineEntity = modelMapper.map(lineDto, OrderLineEntity.class);
|
|
||||||
orderLineEntity.setId(null);
|
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);
|
||||||
|
|
||||||
ProductEntity product = productService.get(lineDto.getProductId());
|
|
||||||
orderLineEntity.setProduct(product); // Устанавливаем продукт для строки заказа
|
|
||||||
entity.addOrderLine(orderLineEntity);
|
|
||||||
}
|
|
||||||
return entity;
|
return entity;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -6,6 +6,7 @@ import com.example.demo.users.model.UserEntity;
|
|||||||
|
|
||||||
import jakarta.persistence.CascadeType;
|
import jakarta.persistence.CascadeType;
|
||||||
import jakarta.persistence.Entity;
|
import jakarta.persistence.Entity;
|
||||||
|
import jakarta.persistence.FetchType;
|
||||||
import jakarta.persistence.JoinColumn;
|
import jakarta.persistence.JoinColumn;
|
||||||
import jakarta.persistence.ManyToMany;
|
import jakarta.persistence.ManyToMany;
|
||||||
import jakarta.persistence.ManyToOne;
|
import jakarta.persistence.ManyToOne;
|
||||||
@ -21,8 +22,9 @@ public class OrderEntity extends BaseEntity {
|
|||||||
@ManyToOne
|
@ManyToOne
|
||||||
@JoinColumn(name = "userId", nullable = false)
|
@JoinColumn(name = "userId", nullable = false)
|
||||||
private UserEntity user;
|
private UserEntity user;
|
||||||
@ManyToMany(cascade = CascadeType.ALL)
|
@ManyToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER)
|
||||||
private final List<OrderLineEntity> lines = new ArrayList<>();
|
private final List<OrderLineEntity> lines = new ArrayList<>();
|
||||||
|
private Double totalPrice; // Поле для общей стоимости заказа
|
||||||
|
|
||||||
public OrderEntity() {
|
public OrderEntity() {
|
||||||
super();
|
super();
|
||||||
@ -44,17 +46,30 @@ public class OrderEntity extends BaseEntity {
|
|||||||
return lines;
|
return lines;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Double getTotalPrice() {
|
||||||
|
return totalPrice;
|
||||||
|
}
|
||||||
|
|
||||||
public void addOrderLine(OrderLineEntity orderLine) {
|
public void addOrderLine(OrderLineEntity orderLine) {
|
||||||
this.lines.add(orderLine);
|
this.lines.add(orderLine);
|
||||||
|
recalculateTotalPrice();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void removeOrderLine(OrderLineEntity orderLine) {
|
public void removeOrderLine(OrderLineEntity orderLine) {
|
||||||
this.lines.remove(orderLine);
|
this.lines.remove(orderLine);
|
||||||
|
recalculateTotalPrice();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void recalculateTotalPrice() {
|
||||||
|
totalPrice = 0.0;
|
||||||
|
for (OrderLineEntity line : lines) {
|
||||||
|
totalPrice += line.getTotalPrice();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int hashCode() {
|
public int hashCode() {
|
||||||
return Objects.hash(id, user, lines);
|
return Objects.hash(id, user, lines, totalPrice);
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings("unlikely-arg-user")
|
@SuppressWarnings("unlikely-arg-user")
|
||||||
@ -67,7 +82,7 @@ public class OrderEntity extends BaseEntity {
|
|||||||
final OrderEntity other = (OrderEntity) obj;
|
final OrderEntity other = (OrderEntity) obj;
|
||||||
return Objects.equals(other.getId(), id)
|
return Objects.equals(other.getId(), id)
|
||||||
&& Objects.equals(other.getUser(), user)
|
&& Objects.equals(other.getUser(), user)
|
||||||
&& Objects.equals(other.getLines(), lines);
|
&& Objects.equals(other.getLines(), lines)
|
||||||
|
&& Objects.equals(other.getTotalPrice(), totalPrice);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -12,7 +12,7 @@ public interface OrderRepository extends CrudRepository<OrderEntity, Long> {
|
|||||||
|
|
||||||
List<OrderEntity> findByUserIdAndLinesProductId(long userId, List<Long> lines);
|
List<OrderEntity> findByUserIdAndLinesProductId(long userId, List<Long> lines);
|
||||||
|
|
||||||
List<OrderEntity> findByLinesProductId(List<Long> lines);
|
List<OrderEntity> findById(long id);
|
||||||
|
|
||||||
List<OrderEntity> findByUserId(long userId);
|
List<OrderEntity> findByUserId(long userId);
|
||||||
|
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
package com.example.demo.orders.service;
|
package com.example.demo.orders.service;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
import org.springframework.transaction.annotation.Transactional;
|
import org.springframework.transaction.annotation.Transactional;
|
||||||
@ -24,7 +25,7 @@ public class OrderService {
|
|||||||
return repository.findByUserIdAndLinesProductId(userId, lines);
|
return repository.findByUserIdAndLinesProductId(userId, lines);
|
||||||
}
|
}
|
||||||
if (userId == 0L && !lines.isEmpty()) {
|
if (userId == 0L && !lines.isEmpty()) {
|
||||||
return repository.findByLinesProductId(lines);
|
return repository.findAll();
|
||||||
}
|
}
|
||||||
if (userId != 0L && lines.isEmpty()) {
|
if (userId != 0L && lines.isEmpty()) {
|
||||||
return repository.findByUserId(userId);
|
return repository.findByUserId(userId);
|
||||||
@ -34,8 +35,12 @@ public class OrderService {
|
|||||||
|
|
||||||
@Transactional(readOnly = true)
|
@Transactional(readOnly = true)
|
||||||
public OrderEntity get(Long id) {
|
public OrderEntity get(Long id) {
|
||||||
return repository.findById(id)
|
// Используем findById репозитория для извлечения OrderEntity по id
|
||||||
.orElseThrow(() -> new NotFoundException(OrderEntity.class, id));
|
Optional<OrderEntity> optionalOrderEntity = repository.findById(id);
|
||||||
|
|
||||||
|
// Используем orElseThrow для выброса исключения, если OrderEntity не найден
|
||||||
|
OrderEntity orderEntity = optionalOrderEntity.orElseThrow(() -> new NotFoundException(OrderEntity.class, id));
|
||||||
|
return orderEntity;
|
||||||
}
|
}
|
||||||
|
|
||||||
public OrderEntity create(OrderEntity entity) {
|
public OrderEntity create(OrderEntity entity) {
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
package com.example.demo.products.service;
|
package com.example.demo.products.service;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
import java.util.stream.StreamSupport;
|
import java.util.stream.StreamSupport;
|
||||||
|
|
||||||
@ -33,6 +32,11 @@ public class ProductService {
|
|||||||
return repository.findById(id).orElseThrow(() -> new NotFoundException(ProductEntity.class, id));
|
return repository.findById(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();
|
||||||
|
}
|
||||||
|
|
||||||
@Transactional
|
@Transactional
|
||||||
public ProductEntity create(ProductEntity entity) {
|
public ProductEntity create(ProductEntity entity) {
|
||||||
if (entity == null) {
|
if (entity == null) {
|
||||||
|
67
src/main/resources/public/css/style.css
Normal file
67
src/main/resources/public/css/style.css
Normal file
@ -0,0 +1,67 @@
|
|||||||
|
html,
|
||||||
|
body {
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
h1 {
|
||||||
|
font-size: 1.5em;
|
||||||
|
}
|
||||||
|
|
||||||
|
h2 {
|
||||||
|
font-size: 1.25em;
|
||||||
|
}
|
||||||
|
|
||||||
|
h3 {
|
||||||
|
font-size: 1.1em;
|
||||||
|
}
|
||||||
|
|
||||||
|
td form {
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
margin-top: -.25em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.button-fixed-width {
|
||||||
|
width: 150px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.button-link {
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.invalid-feedback {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
.w-10 {
|
||||||
|
width: 10% !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.my-navbar {
|
||||||
|
background-color: #3c3c3c !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.my-navbar .link a:hover {
|
||||||
|
text-decoration: underline;
|
||||||
|
}
|
||||||
|
|
||||||
|
.my-navbar .logo {
|
||||||
|
width: 26px;
|
||||||
|
height: 26px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.my-footer {
|
||||||
|
background-color: #2c2c2c;
|
||||||
|
height: 32px;
|
||||||
|
color: rgba(255, 255, 255, 0.5);
|
||||||
|
}
|
||||||
|
|
||||||
|
.cart-image {
|
||||||
|
width: 3.1rem;
|
||||||
|
padding: 0.25rem;
|
||||||
|
border-radius: 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cart-item {
|
||||||
|
height: auto;
|
||||||
|
}
|
37
src/main/resources/static/error.html
Normal file
37
src/main/resources/static/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>
|
71
src/main/resources/templates/default.html
Normal file
71
src/main/resources/templates/default.html
Normal file
@ -0,0 +1,71 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="ru" data-bs-theme="dark" xmlns:th="http://www.thymeleaf.org"
|
||||||
|
xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout"
|
||||||
|
xmlns:sec="http://www.thymeleaf.org/thymeleaf-extras-springsecurity6">
|
||||||
|
|
||||||
|
<head>
|
||||||
|
<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>
|
||||||
|
<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" />
|
||||||
|
<link rel="stylesheet" href="/css/style.css" />
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body class="h-100 d-flex flex-column">
|
||||||
|
<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>
|
||||||
|
Туда сюда и пицца
|
||||||
|
</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"
|
||||||
|
aria-controls="main-navbar" aria-expanded="false" aria-label="Toggle navigation">
|
||||||
|
<span class="navbar-toggler-icon"></span>
|
||||||
|
</button>
|
||||||
|
<div class="collapse navbar-collapse" id="main-navbar">
|
||||||
|
<ul class="navbar-nav me-auto link" th:with="activeLink=${#objects.nullSafe(servletPath, '')}">
|
||||||
|
<th:block sec:authorize="hasRole('ADMIN')">
|
||||||
|
<a class="nav-link" href="/admin/user"
|
||||||
|
th:classappend="${activeLink.startsWith('/admin/user') ? 'active' : ''}">
|
||||||
|
Пользователи
|
||||||
|
</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>
|
||||||
|
<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>
|
||||||
|
</ul>
|
||||||
|
<ul class="navbar-nav" th:if="${not #strings.isEmpty(userName)}">
|
||||||
|
<form th:action="@{/logout}" method="post">
|
||||||
|
<button type="submit" class="navbar-brand nav-link" onclick="return confirm('Вы уверены?')">
|
||||||
|
Выход ([[${userName}]])
|
||||||
|
</button>
|
||||||
|
</form>
|
||||||
|
<a class="navbar-brand" href="/cart">
|
||||||
|
<i class="bi bi-cart2 d-inline-block align-top me-1 logo"></i>
|
||||||
|
[[${#numbers.formatDecimal(totalCart, 1, 2)}]] ₽
|
||||||
|
</a>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</th:block>
|
||||||
|
</div>
|
||||||
|
</nav>
|
||||||
|
<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())}]]
|
||||||
|
</footer>
|
||||||
|
</body>
|
||||||
|
|
||||||
|
</html>
|
44
src/main/resources/templates/login.html
Normal file
44
src/main/resources/templates/login.html
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
<!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">
|
||||||
|
<form action="#" th:action="@{/login}" method="post">
|
||||||
|
<div th:if="${param.error}" class="alert alert-danger">
|
||||||
|
Неверный логин или пароль
|
||||||
|
</div>
|
||||||
|
<div th:if="${param.logout}" class="alert alert-success">
|
||||||
|
Выход успешно произведен
|
||||||
|
</div>
|
||||||
|
<div th:if="${param.signup}" class="alert alert-success">
|
||||||
|
Пользователь успешно создан
|
||||||
|
</div>
|
||||||
|
<div class="mb-3">
|
||||||
|
<label for="username" class="form-label">Имя пользователя</label>
|
||||||
|
<input type="text" id="username" name="username" class="form-control" required minlength="3"
|
||||||
|
maxlength="20">
|
||||||
|
</div>
|
||||||
|
<div class="mb-3">
|
||||||
|
<label for="password" class="form-label">Пароль</label>
|
||||||
|
<input type="password" id="password" name="password" class="form-control" required minlength="3"
|
||||||
|
maxlength="20">
|
||||||
|
</div>
|
||||||
|
<div class="form-check mb-3">
|
||||||
|
<input class="form-check-input" type="checkbox" id="remember-me" name="remember-me" checked>
|
||||||
|
<label class="form-check-label" for="remember-me">Запомнить меня</label>
|
||||||
|
</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="/signup">Регистрация</a>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</main>
|
||||||
|
</body>
|
||||||
|
|
||||||
|
</html>
|
||||||
|
|
||||||
|
</html>
|
37
src/main/resources/templates/signup.html
Normal file
37
src/main/resources/templates/signup.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">
|
||||||
|
<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="password" class="form-label">Пароль</label>
|
||||||
|
<input type="password" th:field="*{password}" id="password" class="form-control">
|
||||||
|
<div th:if="${#fields.hasErrors('password')}" th:errors="*{password}" class="invalid-feedback"></div>
|
||||||
|
</div>
|
||||||
|
<div class="mb-3">
|
||||||
|
<label for="passwordConfirm" class="form-label">Пароль (подтверждение)</label>
|
||||||
|
<input type="password" th:field="*{passwordConfirm}" id="passwordConfirm" class="form-control">
|
||||||
|
<div th:if="${#fields.hasErrors('passwordConfirm')}" th:errors="*{passwordConfirm}"
|
||||||
|
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="/">Отмена</a>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</main>
|
||||||
|
</body>
|
||||||
|
|
||||||
|
</html>
|
||||||
|
|
||||||
|
</html>
|
28
src/main/resources/templates/type-edit.html
Normal file
28
src/main/resources/templates/type-edit.html
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
<!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">
|
||||||
|
<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>
|
||||||
|
<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-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/type">Отмена</a>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</main>
|
||||||
|
</body>
|
||||||
|
|
||||||
|
</html>
|
50
src/main/resources/templates/type.html
Normal file
50
src/main/resources/templates/type.html
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="ru" xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout" layout:decorate="~{default}">
|
||||||
|
|
||||||
|
<head>
|
||||||
|
<title>Типы заказов</title>
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
<main layout:fragment="content">
|
||||||
|
<th:block th:switch="${items.size()}">
|
||||||
|
<h2 th:case="0">Данные отсутствуют</h2>
|
||||||
|
<th:block th:case="*">
|
||||||
|
<h2>Типы заказов</h2>
|
||||||
|
<div>
|
||||||
|
<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-10"></th>
|
||||||
|
<th scope="col" class="w-10"></th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<tr th:each="type : ${items}">
|
||||||
|
<th scope="row" th:text="${type.id}"></th>
|
||||||
|
<td th:text="${type.name}"></td>
|
||||||
|
<td>
|
||||||
|
<form th:action="@{/admin/type/edit/{id}(id=${type.id})}" method="get">
|
||||||
|
<button type="submit" class="btn btn-link button-link">Редактировать</button>
|
||||||
|
</form>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<form th:action="@{/admin/type/delete/{id}(id=${type.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>
|
@ -1,72 +1,98 @@
|
|||||||
package com.example.demo;
|
package com.example.demo;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
import org.junit.jupiter.api.Assertions;
|
|
||||||
import org.junit.jupiter.api.MethodOrderer;
|
|
||||||
import org.junit.jupiter.api.Order;
|
|
||||||
import org.junit.jupiter.api.Test;
|
|
||||||
import org.junit.jupiter.api.TestMethodOrder;
|
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
|
||||||
import org.springframework.boot.test.context.SpringBootTest;
|
|
||||||
import com.example.demo.core.error.NotFoundException;
|
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.model.OrderEntity;
|
||||||
import com.example.demo.orders.service.OrderService;
|
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 org.junit.jupiter.api.*;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.boot.test.context.SpringBootTest;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
@SpringBootTest
|
@SpringBootTest
|
||||||
@TestMethodOrder(MethodOrderer.OrderAnnotation.class)
|
|
||||||
class OrderServiceTests {
|
class OrderServiceTests {
|
||||||
@Autowired
|
@Autowired
|
||||||
private OrderService orderService;
|
private OrderService orderService;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private UserService userService;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private ProductService productService;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private TypeService typeService;
|
||||||
|
|
||||||
|
private OrderEntity order;
|
||||||
|
|
||||||
|
@BeforeEach
|
||||||
|
void createData() {
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
@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());
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void getTest() {
|
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, () -> orderService.get(0L));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@Order(1)
|
|
||||||
void createTest() {
|
void createTest() {
|
||||||
orderService.create(new OrderEntity(null));
|
// Проверяем, что созданный заказ совпадает с ожидаемым
|
||||||
@SuppressWarnings({ "rawtypes", "unchecked" })
|
Assertions.assertNotNull(order.getId());
|
||||||
List<OrderLineEntity> lines = new ArrayList();
|
OrderEntity foundOrder = orderService.get(order.getId());
|
||||||
lines.add(new OrderLineEntity(null, 4));
|
Assertions.assertEquals(order, foundOrder);
|
||||||
lines.add(new OrderLineEntity(null, 5));
|
|
||||||
lines.add(new OrderLineEntity(null, 6));
|
|
||||||
lines.add(new OrderLineEntity(null, 7));
|
|
||||||
// Создаем тестовую сущность OrderEntity
|
|
||||||
OrderEntity testOrder = new OrderEntity(null);
|
|
||||||
// Вызываем метод create() и сохраняем созданную сущность
|
|
||||||
OrderEntity createdOrder = orderService.create(testOrder);
|
|
||||||
// Проверяем, что метод create() вернул не null
|
|
||||||
Assertions.assertNotNull(createdOrder);
|
|
||||||
// Проверяем, что созданная сущность имеет назначенный ID
|
|
||||||
Assertions.assertNotNull(createdOrder.getId());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@Order(2)
|
|
||||||
void updateTest() {
|
void updateTest() {
|
||||||
// Получаем сущность OrderEntity для обновления
|
// Обновляем заказ и проверяем, что он изменился
|
||||||
OrderEntity existingOrder = orderService.get(1L);
|
OrderEntity updatedOrder = orderService.update(order.getId(), order);
|
||||||
// Вызываем метод update() и сохраняем обновленную сущность
|
|
||||||
OrderEntity updatedOrder = orderService.update(1L, existingOrder);
|
|
||||||
// Проверяем, что метод update() вернул не null
|
|
||||||
Assertions.assertNotNull(updatedOrder);
|
Assertions.assertNotNull(updatedOrder);
|
||||||
|
Assertions.assertEquals(order, updatedOrder);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@Order(3)
|
|
||||||
void deleteTest() {
|
void deleteTest() {
|
||||||
// Удаляем сущность OrderEntity по ее ID
|
// Удаляем заказ и проверяем, что его нет в базе данных
|
||||||
OrderEntity deletedOrder = orderService.delete(1L);
|
orderService.delete(order.getId());
|
||||||
// Проверяем, что метод delete() вернул не null
|
Assertions.assertThrows(NotFoundException.class, () -> orderService.get(order.getId()));
|
||||||
Assertions.assertNotNull(deletedOrder);
|
|
||||||
// Проверяем, что удаленная сущность имеет тот же ID, что и удаленная
|
|
||||||
Assertions.assertEquals(1L, deletedOrder.getId());
|
|
||||||
// Проверяем, что сущность больше не существует в репозитории
|
|
||||||
Assertions.assertThrows(NotFoundException.class, () -> orderService.get(1L));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
package com.example.demo;
|
package com.example.demo;
|
||||||
|
|
||||||
|
import org.junit.jupiter.api.AfterEach;
|
||||||
import org.junit.jupiter.api.Assertions;
|
import org.junit.jupiter.api.Assertions;
|
||||||
|
import org.junit.jupiter.api.BeforeEach;
|
||||||
import org.junit.jupiter.api.MethodOrderer.OrderAnnotation;
|
import org.junit.jupiter.api.MethodOrderer.OrderAnnotation;
|
||||||
import org.junit.jupiter.api.Order;
|
import org.junit.jupiter.api.Order;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
@ -18,6 +20,23 @@ class UserServiceTests {
|
|||||||
@Autowired
|
@Autowired
|
||||||
private UserService userService;
|
private UserService userService;
|
||||||
|
|
||||||
|
@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);
|
||||||
|
}
|
||||||
|
|
||||||
|
@AfterEach
|
||||||
|
void clearData() {
|
||||||
|
userService.delete(1L);
|
||||||
|
userService.delete(2L);
|
||||||
|
userService.delete(3L);
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void getTest() {
|
void getTest() {
|
||||||
Assertions.assertThrows(NotFoundException.class, () -> userService.get(0L));
|
Assertions.assertThrows(NotFoundException.class, () -> userService.get(0L));
|
||||||
@ -26,12 +45,10 @@ class UserServiceTests {
|
|||||||
@Test
|
@Test
|
||||||
@Order(1)
|
@Order(1)
|
||||||
void createTest() {
|
void createTest() {
|
||||||
userService.create(new UserEntity("John", "Doe", "gge@fkgjfdj", "password"));
|
final UserEntity last = userService
|
||||||
userService.create(new UserEntity("Alex", "Kryukov", "fhegehr@ghsjg.com", "password"));
|
.create(new UserEntity("Alex", "selivanov", "fheg123ehr@ghsjg.com", "password"));
|
||||||
final UserEntity last = userService.create(new UserEntity("Alex", "selivanov", "fhegehr@ghsjg.com",
|
Assertions.assertEquals(4, userService.getAll().size());
|
||||||
"password"));
|
Assertions.assertEquals(last, userService.get(4L));
|
||||||
Assertions.assertEquals(3, userService.getAll().size());
|
|
||||||
Assertions.assertEquals(last, userService.get(3L));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
Loading…
x
Reference in New Issue
Block a user