Compare commits
18 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
80293b61d2 | ||
fbc3160393 | |||
5edb2b497e | |||
d5644b1a99 | |||
61d964259f | |||
fe7076c6f4 | |||
8e3a414847 | |||
743f35ffef | |||
6699eda085 | |||
5d16b4a2cd | |||
98d5474e3c | |||
56bc353670 | |||
4b358e7975 | |||
|
b5775f0abb | ||
e95c311912 | |||
efe8945d3b | |||
194ff8ca33 | |||
|
15bc523a42 |
4
.gitignore
vendored
4
.gitignore
vendored
@ -18,6 +18,9 @@ bin/
|
|||||||
!**/src/test/**/bin/
|
!**/src/test/**/bin/
|
||||||
|
|
||||||
### IntelliJ IDEA ###
|
### IntelliJ IDEA ###
|
||||||
|
*.png
|
||||||
|
*.jpeg
|
||||||
|
*.webp
|
||||||
.idea
|
.idea
|
||||||
*.iws
|
*.iws
|
||||||
*.iml
|
*.iml
|
||||||
@ -35,3 +38,4 @@ out/
|
|||||||
|
|
||||||
### VS Code ###
|
### VS Code ###
|
||||||
.vscode/
|
.vscode/
|
||||||
|
/frontend/node_modules/
|
||||||
|
18
build.gradle
18
build.gradle
@ -13,7 +13,25 @@ repositories {
|
|||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
|
annotationProcessor "org.springframework.boot:spring-boot-configuration-processor"
|
||||||
implementation 'org.springframework.boot:spring-boot-starter-web'
|
implementation 'org.springframework.boot:spring-boot-starter-web'
|
||||||
|
implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
|
||||||
|
implementation 'org.springframework.boot:spring-boot-starter-thymeleaf'
|
||||||
|
implementation 'org.springframework.boot:spring-boot-starter-security'
|
||||||
|
implementation 'com.h2database:h2:2.1.210'
|
||||||
|
implementation 'com.auth0:java-jwt:4.4.0'
|
||||||
|
implementation 'org.thymeleaf.extras:thymeleaf-extras-springsecurity5'
|
||||||
|
implementation 'org.springframework.boot:spring-boot-devtools'
|
||||||
|
implementation 'nz.net.ultraq.thymeleaf:thymeleaf-layout-dialect'
|
||||||
|
implementation 'org.webjars:bootstrap:5.1.3'
|
||||||
|
implementation 'org.webjars:jquery:3.6.0'
|
||||||
|
implementation 'org.webjars:font-awesome:6.1.0'
|
||||||
|
implementation 'jakarta.validation:jakarta.validation-api:3.0.0'
|
||||||
|
|
||||||
|
implementation group: 'org.springdoc', name: 'springdoc-openapi-ui', version: '1.6.5'
|
||||||
|
|
||||||
|
implementation 'org.hibernate.validator:hibernate-validator:6.0.17.Final'
|
||||||
|
|
||||||
testImplementation 'org.springframework.boot:spring-boot-starter-test'
|
testImplementation 'org.springframework.boot:spring-boot-starter-test'
|
||||||
}
|
}
|
||||||
|
|
||||||
|
24
frontend/spa-vue/.gitignore
vendored
Normal file
24
frontend/spa-vue/.gitignore
vendored
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
# Logs
|
||||||
|
logs
|
||||||
|
*.log
|
||||||
|
npm-debug.log*
|
||||||
|
yarn-debug.log*
|
||||||
|
yarn-error.log*
|
||||||
|
pnpm-debug.log*
|
||||||
|
lerna-debug.log*
|
||||||
|
|
||||||
|
node_modules
|
||||||
|
dist
|
||||||
|
dist-ssr
|
||||||
|
*.local
|
||||||
|
|
||||||
|
# Editor directories and files
|
||||||
|
.vscode/*
|
||||||
|
!.vscode/extensions.json
|
||||||
|
.idea
|
||||||
|
.DS_Store
|
||||||
|
*.suo
|
||||||
|
*.ntvs*
|
||||||
|
*.njsproj
|
||||||
|
*.sln
|
||||||
|
*.sw?
|
7
frontend/spa-vue/README.md
Normal file
7
frontend/spa-vue/README.md
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
# Vue 3 + Vite
|
||||||
|
|
||||||
|
This template should help get you started developing with Vue 3 in Vite. The template uses Vue 3 `<script setup>` SFCs, check out the [script setup docs](https://v3.vuejs.org/api/sfc-script-setup.html#sfc-script-setup) to learn more.
|
||||||
|
|
||||||
|
## Recommended IDE Setup
|
||||||
|
|
||||||
|
- [VS Code](https://code.visualstudio.com/) + [Volar](https://marketplace.visualstudio.com/items?itemName=Vue.volar) (and disable Vetur) + [TypeScript Vue Plugin (Volar)](https://marketplace.visualstudio.com/items?itemName=Vue.vscode-typescript-vue-plugin).
|
35
frontend/spa-vue/data.json
Normal file
35
frontend/spa-vue/data.json
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
{
|
||||||
|
"Devices": [
|
||||||
|
{
|
||||||
|
"name": "Компьютер"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Телефон"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Видеокарта"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"Products": [
|
||||||
|
{
|
||||||
|
"type": "Компьютер",
|
||||||
|
"price": 435,
|
||||||
|
"count": 345,
|
||||||
|
"id": 5
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 9,
|
||||||
|
"type": "123123",
|
||||||
|
"price": 324,
|
||||||
|
"count": 124124,
|
||||||
|
"ame": "345345"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 10,
|
||||||
|
"type": "123123",
|
||||||
|
"price": 2342,
|
||||||
|
"count": 123,
|
||||||
|
"ame": "123"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
14
frontend/spa-vue/index.html
Normal file
14
frontend/spa-vue/index.html
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8" />
|
||||||
|
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
|
<title>Главная страница</title>
|
||||||
|
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.2.2/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-Zenh87qX5JnK2Jl0vWa8Ck2rdkQ2Bzep5IDxbcnCeuOxjzrPF/et3URy9Bv1WTRi" crossorigin="anonymous">
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div id="app"></div>
|
||||||
|
<script type="module" src="/src/main.js"></script>
|
||||||
|
</body>
|
||||||
|
</html>
|
4977
frontend/spa-vue/package-lock.json
generated
Normal file
4977
frontend/spa-vue/package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
26
frontend/spa-vue/package.json
Normal file
26
frontend/spa-vue/package.json
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
{
|
||||||
|
"name": "spa-vue",
|
||||||
|
"private": true,
|
||||||
|
"version": "1.0.0",
|
||||||
|
"type": "module",
|
||||||
|
"scripts": {
|
||||||
|
"dev": "vite",
|
||||||
|
"fake-server": "json-server --watch data.json -p 8079",
|
||||||
|
"start": "npm-run-all --parallel dev fake-server",
|
||||||
|
"build": "vite build",
|
||||||
|
"preview": "vite preview"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"vue": "^3.2.41",
|
||||||
|
"vue-router": "^4.1.6",
|
||||||
|
"axios": "^1.1.3",
|
||||||
|
"bootstrap": "^5.2.2",
|
||||||
|
"@fortawesome/fontawesome-free": "^6.2.1"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"vite": "^3.2.3",
|
||||||
|
"@vitejs/plugin-vue": "^3.2.0",
|
||||||
|
"npm-run-all": "^4.1.5",
|
||||||
|
"json-server": "^0.17.1"
|
||||||
|
}
|
||||||
|
}
|
17
frontend/spa-vue/src/App.vue
Normal file
17
frontend/spa-vue/src/App.vue
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
<script>
|
||||||
|
import Header from "./components/Header.vue";
|
||||||
|
export default{
|
||||||
|
components:{
|
||||||
|
Header
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<Header></Header>
|
||||||
|
<router-view>
|
||||||
|
|
||||||
|
</router-view>
|
||||||
|
</template>
|
||||||
|
|
14
frontend/spa-vue/src/banner.css
Normal file
14
frontend/spa-vue/src/banner.css
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
#banner {
|
||||||
|
margin: 15px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
flex-direction: column;
|
||||||
|
min-height: 300px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#banner img {
|
||||||
|
border-radius: 15px;
|
||||||
|
width: 100%;
|
||||||
|
max-height: 500px;
|
||||||
|
max-width: 70%;
|
||||||
|
}
|
41
frontend/spa-vue/src/components/Header.vue
Normal file
41
frontend/spa-vue/src/components/Header.vue
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
<template>
|
||||||
|
<nav class="navbar navbar-expand-lg navbar-light bg-light">
|
||||||
|
<div class="container-fluid">
|
||||||
|
<div class="collapse navbar-collapse" id="navbarNav">
|
||||||
|
<ul class="navbar-nav justify-content-center">
|
||||||
|
<li class="nav-item">
|
||||||
|
<router-link to="/manufacturer" class="nav-link">Производители</router-link>
|
||||||
|
<router-link to="/category" class="nav-link">Категории</router-link>
|
||||||
|
<router-link to="/product" class="nav-link">Продукты</router-link>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</nav>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.navbar-brand {
|
||||||
|
font-size: 2rem;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
.nav-link {
|
||||||
|
color: black;
|
||||||
|
font-weight: bold;
|
||||||
|
text-transform: uppercase;
|
||||||
|
transition: color 0.3s ease-in-out;
|
||||||
|
}
|
||||||
|
|
||||||
|
.nav-link:hover {
|
||||||
|
color: #dc3545;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
|
||||||
|
}
|
||||||
|
</script>
|
7
frontend/spa-vue/src/main.js
Normal file
7
frontend/spa-vue/src/main.js
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
import { createApp } from 'vue'
|
||||||
|
import './styles.css'
|
||||||
|
import './banner.css'
|
||||||
|
import App from './App.vue'
|
||||||
|
import router from "./routes.js"
|
||||||
|
|
||||||
|
createApp(App).use(router).mount('#app')
|
6
frontend/spa-vue/src/models/Category.js
Normal file
6
frontend/spa-vue/src/models/Category.js
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
export default class Category{
|
||||||
|
constructor(data) {
|
||||||
|
this.id = data?.id;
|
||||||
|
this.name = data?.name;
|
||||||
|
}
|
||||||
|
}
|
7
frontend/spa-vue/src/models/Manufacturer.js
Normal file
7
frontend/spa-vue/src/models/Manufacturer.js
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
export default class Manufacturer{
|
||||||
|
constructor(data) {
|
||||||
|
this.id = data?.id;
|
||||||
|
this.name = data?.name;
|
||||||
|
this.address = data?.address;
|
||||||
|
}
|
||||||
|
}
|
9
frontend/spa-vue/src/models/Product.js
Normal file
9
frontend/spa-vue/src/models/Product.js
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
export default class Product{
|
||||||
|
constructor(data){
|
||||||
|
this.id = data?.id;
|
||||||
|
this.name = data?.name;
|
||||||
|
this.price = data?.price;
|
||||||
|
this.photo = data?.photo;
|
||||||
|
this.categoryId = data?.categoryId;
|
||||||
|
}
|
||||||
|
}
|
171
frontend/spa-vue/src/pages/categories.vue
Normal file
171
frontend/spa-vue/src/pages/categories.vue
Normal file
@ -0,0 +1,171 @@
|
|||||||
|
<template>
|
||||||
|
<div class="container mt-4">
|
||||||
|
<h1 class="text-center mb-4">Categories Table</h1>
|
||||||
|
<button class="btn btn-primary mr-2" @click="openModal('create')">Добавить</button>
|
||||||
|
<table class="table table-striped">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>Название</th>
|
||||||
|
<th></th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<tr v-for="categor in categories" :key="categor.id">
|
||||||
|
<td>{{ categor.name }}</td>
|
||||||
|
<td>
|
||||||
|
<td>
|
||||||
|
<button class="btn btn-primary mr-2" @click="openModal('edit', categor)">Изменить</button>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<button class="btn btn-danger" @click="deleteCategory(categor.id)">Удалить</button>
|
||||||
|
</td>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
<div class="modal" tabindex="-1" id="editModal">
|
||||||
|
<div class="modal-dialog">
|
||||||
|
<div class="modal-content">
|
||||||
|
<div class="modal-header">
|
||||||
|
<h5 class="modal-title">Категория</h5>
|
||||||
|
</div>
|
||||||
|
<div class="modal-body">
|
||||||
|
<form>
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="name">Название:</label>
|
||||||
|
<input type="text" class="form-control" id="name" name="name" v-model="editedCategory.name">
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
<div class="modal-footer">
|
||||||
|
<button type="button" class="btn btn-secondary" data-bs-dismiss="editModal" @click="closeModal()">Закрыть</button>
|
||||||
|
<button type="button" class="btn btn-primary" v-if="editedCategory.status === 'create'" @click="addCategory(editedCategory)">Создать</button>
|
||||||
|
<button type="button" class="btn btn-primary" v-else @click="editCategory(editedCategory)">Сохранить</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<!-- Модальное окно для просмотра студентов в группе-->
|
||||||
|
<div class="modal" tabindex="-1" id="ModelForProducts">
|
||||||
|
<div class="modal-dialog">
|
||||||
|
<div class="modal-content">
|
||||||
|
<div class="modal-header">
|
||||||
|
<h5 class="modal-title">Продукты</h5>
|
||||||
|
</div>
|
||||||
|
<div class="modal-body">
|
||||||
|
<form>
|
||||||
|
<div class="form-group">
|
||||||
|
<table class="table table-striped">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>Название</th>
|
||||||
|
<th>Цена</th>
|
||||||
|
<th>Фото</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<tr v-for="prod in products" :key="prod.id">
|
||||||
|
<td>{{ prod.name }}</td>
|
||||||
|
<td>{{ prod.price }}</td>
|
||||||
|
<td>{{ prod.photo }}</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
<div class="modal-footer">
|
||||||
|
<button type="button" class="btn btn-secondary" data-bs-dismiss="ModelForStudents" @click="closeModelForProducts()">Закрыть</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<script>
|
||||||
|
import 'axios';
|
||||||
|
import axios from "axios";
|
||||||
|
import Category from "../models/Category"
|
||||||
|
import Product from '../models/Product';
|
||||||
|
export default {
|
||||||
|
created() {
|
||||||
|
this.getCategories();
|
||||||
|
},
|
||||||
|
mounted() {
|
||||||
|
const addModal = document.getElementById('editModal');
|
||||||
|
addModal.addEventListener('shown.bs.modal', function () {
|
||||||
|
})
|
||||||
|
},
|
||||||
|
|
||||||
|
data() {
|
||||||
|
return{
|
||||||
|
categories: [],
|
||||||
|
URL: "http://localhost:8080/",
|
||||||
|
category: new Category(),
|
||||||
|
editedCategory: new Category(),
|
||||||
|
products: [],
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
getCategories(){
|
||||||
|
axios.get(this.URL + "category")
|
||||||
|
.then(response => {
|
||||||
|
this.categories = response.data;
|
||||||
|
console.log(response.data);
|
||||||
|
})
|
||||||
|
.catch(error => {
|
||||||
|
console.log(error);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
addCategory(category) {
|
||||||
|
console.log(category);
|
||||||
|
axios
|
||||||
|
.post(this.URL + "category", category)
|
||||||
|
.then(() => {
|
||||||
|
this.getCategories();
|
||||||
|
this.closeModal();
|
||||||
|
})
|
||||||
|
.catch((error) => {
|
||||||
|
console.log(error);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
deleteCategory(id){
|
||||||
|
axios.delete(this.URL + `category/${id}`)
|
||||||
|
.then(() =>{
|
||||||
|
this.getCategories();
|
||||||
|
})
|
||||||
|
},
|
||||||
|
openModal(status, category = null) {
|
||||||
|
if (status === "create") {
|
||||||
|
this.editedCategory = new Category();
|
||||||
|
this.editedCategory.status = "create";
|
||||||
|
} else if (status === "edit" && category) {
|
||||||
|
this.editedCategory = { ...category };
|
||||||
|
this.editedCategory.status = "edit";
|
||||||
|
}
|
||||||
|
|
||||||
|
document.getElementById("editModal").style.display = "block";
|
||||||
|
},
|
||||||
|
closeModal() {
|
||||||
|
document.getElementById("editModal").style.display = "none";
|
||||||
|
},
|
||||||
|
editCategory(category) {
|
||||||
|
axios.put(this.URL + `category/${category.id}`, category)
|
||||||
|
.then(() => {
|
||||||
|
const index = this.categories.findIndex((s) => s.id === category.id);
|
||||||
|
if (index !== -1) {
|
||||||
|
this.categories[index] = { ...category };
|
||||||
|
}
|
||||||
|
this.closeModal();
|
||||||
|
this.getCategories();
|
||||||
|
})
|
||||||
|
.catch((error) => {
|
||||||
|
console.log(error);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
<style>
|
||||||
|
|
||||||
|
</style>
|
134
frontend/spa-vue/src/pages/manufacturers.vue
Normal file
134
frontend/spa-vue/src/pages/manufacturers.vue
Normal file
@ -0,0 +1,134 @@
|
|||||||
|
<template>
|
||||||
|
<div class="container mt-4">
|
||||||
|
<h1 class="text-center mb-4">Manufacturer Table</h1>
|
||||||
|
<button class="btn btn-primary mr-2" @click="openModal('create')">Добавить</button>
|
||||||
|
<table class="table table-striped">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>Имя</th>
|
||||||
|
<th>Адресс</th>
|
||||||
|
<th></th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<tr v-for="manufs in manufacturers" :key="manufs.id">
|
||||||
|
<td>{{ manufs.name }}</td>
|
||||||
|
<td>{{ manufs.address }}</td>
|
||||||
|
<td>
|
||||||
|
<td>
|
||||||
|
<button class="btn btn-primary mr-2" @click="openModal('edit', manufs)">Изменить</button>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<button class="btn btn-danger" @click="deleteManufacturer(manufs.id)">Удалить</button>
|
||||||
|
</td>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
<div class="modal" tabindex="-1" id="editModal">
|
||||||
|
<div class="modal-dialog">
|
||||||
|
<div class="modal-content">
|
||||||
|
<div class="modal-header">
|
||||||
|
<h5 class="modal-title">Производитель</h5>
|
||||||
|
</div>
|
||||||
|
<div class="modal-body">
|
||||||
|
<form>
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="name">Имя:</label>
|
||||||
|
<input type="text" class="form-control" id="name" name="name" v-model="manufacturer.name">
|
||||||
|
<label for="adress">Адресс:</label>
|
||||||
|
<input type="text" class="form-control" id="adress" name="address" v-model="manufacturer.address">
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
<div class="modal-footer">
|
||||||
|
<button type="button" class="btn btn-secondary" data-bs-dismiss="editModal" @click="closeModal()">Закрыть</button>
|
||||||
|
<button type="button" class="btn btn-primary" v-if="manufacturer.status === 'create'" @click="addManufacturer(manufacturer)">Создать</button>
|
||||||
|
<button type="button" class="btn btn-primary" v-else @click="editManufacturer(manufacturer)">Сохранить</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import 'axios';
|
||||||
|
import axios from "axios";
|
||||||
|
import Manufacturer from "../models/Manufacturer"
|
||||||
|
export default {
|
||||||
|
created() {
|
||||||
|
this.getManufacturers();
|
||||||
|
},
|
||||||
|
mounted() {
|
||||||
|
const addModal = document.getElementById('editModal');
|
||||||
|
addModal.addEventListener('shown.bs.modal', function () {
|
||||||
|
})
|
||||||
|
},
|
||||||
|
|
||||||
|
data() {
|
||||||
|
return{
|
||||||
|
manufacturers: [],
|
||||||
|
URL: "http://localhost:8080/",
|
||||||
|
manufacturer: new Manufacturer(),
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
getManufacturers(){
|
||||||
|
axios.get(this.URL + "manufacturer")
|
||||||
|
.then(response => {
|
||||||
|
this.manufacturers = response.data;
|
||||||
|
console.log(response.data);
|
||||||
|
})
|
||||||
|
.catch(error => {
|
||||||
|
console.log(error);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
addManufacturer(manufacturer){
|
||||||
|
console.log(this.manufacturers);
|
||||||
|
axios.post(this.URL + "manufacturer", manufacturer)
|
||||||
|
.then(() => {
|
||||||
|
this.getManufacturers();
|
||||||
|
this.closeModal();
|
||||||
|
})
|
||||||
|
.catch(error => {
|
||||||
|
console.log(error);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
deleteManufacturer(id){
|
||||||
|
axios.delete(this.URL + `manufacturer/${id}`)
|
||||||
|
.then(() =>{
|
||||||
|
this.getManufacturers();
|
||||||
|
})
|
||||||
|
},
|
||||||
|
editManufacturer(manufacturer){
|
||||||
|
axios.put(this.URL + `manufacturer/${manufacturer.id}`, manufacturer)
|
||||||
|
.then(() =>{
|
||||||
|
const index = this.manufacturers.findIndex((m) => m.id === manufacturer.id);
|
||||||
|
if (index !== -1) {
|
||||||
|
this.manufacturers[index] = { ...manufacturer };
|
||||||
|
}
|
||||||
|
this.closeModal();
|
||||||
|
this.getManufacturers();
|
||||||
|
})
|
||||||
|
this.closeModal();
|
||||||
|
},
|
||||||
|
openModal(status, manufacturer = null) {
|
||||||
|
if (status === "create") {
|
||||||
|
this.manufacturer = new Manufacturer();
|
||||||
|
this.manufacturer.status = "create";
|
||||||
|
} else if (status === "edit" && manufacturer) {
|
||||||
|
this.manufacturer = { ...manufacturer };
|
||||||
|
this.manufacturer.status = "edit";
|
||||||
|
}
|
||||||
|
document.getElementById("editModal").style.display = "block";
|
||||||
|
},
|
||||||
|
closeModal() {
|
||||||
|
document.getElementById("editModal").style.display = "none";
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
<style>
|
||||||
|
|
||||||
|
</style>
|
274
frontend/spa-vue/src/pages/products.vue
Normal file
274
frontend/spa-vue/src/pages/products.vue
Normal file
@ -0,0 +1,274 @@
|
|||||||
|
<template>
|
||||||
|
<div class="container mt-4">
|
||||||
|
<h1 class="text-center mb-4">Product Table</h1>
|
||||||
|
<button class="btn btn-primary mr-2" @click="openModal('create')">Добавить</button>
|
||||||
|
<table class="table table-striped">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>Название</th>
|
||||||
|
<th>Цена</th>
|
||||||
|
<th>Фото</th>
|
||||||
|
<th>Категория</th>
|
||||||
|
<th></th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<tr v-for="prdct in products" :key="prdct.id">
|
||||||
|
<td>{{ prdct.name }}</td>
|
||||||
|
<td>{{ prdct.price }}</td>
|
||||||
|
<td> <img :src = "prdct.photo"></td>
|
||||||
|
<td>{{ prdct && categories.find(ctgr => ctgr.id === prdct.categoryId).name }}</td>
|
||||||
|
<td>
|
||||||
|
<td>
|
||||||
|
<button class="btn btn-primary mr-2" @click="openModal('edit', prdct)">Изменить</button>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<button class="btn btn-danger" @click="deleteProduct(prdct.id)">Удалить</button>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<button class="btn btn-primary" @click="product = prdct; openManyToManyModal()">Производители</button>
|
||||||
|
</td>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
<!-- Модальное окно для добавления и изменения-->
|
||||||
|
<div class="modal" tabindex="-1" id="editModal">
|
||||||
|
<div class="modal-dialog">
|
||||||
|
<div class="modal-content">
|
||||||
|
<div class="modal-header">
|
||||||
|
<h5 class="modal-title">Продукт</h5>
|
||||||
|
</div>
|
||||||
|
<div class="modal-body">
|
||||||
|
<form>
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="name">Имя:</label>
|
||||||
|
<input type="text" class="form-control" id="name" name="name" v-model="product.name">
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="price">Цена:</label>
|
||||||
|
<input type="text" class="form-control" id="price" name="price" v-model="product.price">
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<input class="form-control" type="file" id="cover" name="cover" @change="toBase64">
|
||||||
|
<img :src="product.photo" class="img-thumbnail"/>
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="category">Категория:</label>
|
||||||
|
<select v-model="product.categoryId" class="form-control">
|
||||||
|
<option v-for="(category, index) in categories" :key="index" :value="category.id">
|
||||||
|
{{ category.name }}
|
||||||
|
</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
<div class="modal-footer">
|
||||||
|
<button type="button" class="btn btn-secondary" data-bs-dismiss="editModal" @click="closeModal">Закрыть</button>
|
||||||
|
<button type="button" class="btn btn-primary" v-if="product.status === 'create'" @click="addProduct">Создать</button>
|
||||||
|
<button type="button" class="btn btn-primary" v-else @click="editProduct(product)">Сохранить</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="modal" tabindex="-1" id="manyToManyModal">
|
||||||
|
<div class="modal-dialog">
|
||||||
|
<div class="modal-content">
|
||||||
|
<div class="modal-header">
|
||||||
|
<h5 class="modal-title">Add Manufacturers to Product</h5>
|
||||||
|
</div>
|
||||||
|
<div class="modal-body">
|
||||||
|
<form>
|
||||||
|
<table class="table table-striped">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>Название:</th>
|
||||||
|
<th>Редактировать запись:</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<tr v-for="prod in productManufacturers" :key="prod.id">
|
||||||
|
<td>{{ prod.name }}</td>
|
||||||
|
<td>
|
||||||
|
<button class="btn btn-primary" type="button" @click="removeManufacturer(prod.id)">Удалить</button>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
<div class="input-group mb-3">
|
||||||
|
<select class="form-select" v-model="manufacturerId">
|
||||||
|
<option v-for="mnfctr in manufacturers" :key="mnfctr.id" :value="mnfctr.id">{{ mnfctr.name }}</option>
|
||||||
|
</select>
|
||||||
|
<button class="btn btn-outline-secondary" type="button" @click="addManufacturer()">Добавить</button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
<div class="modal-footer">
|
||||||
|
<button type="button" class="btn btn-secondary" data-bs-dismiss="editModal" @click="closeManyToManyModal()">Закрыть</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<script>
|
||||||
|
import 'axios';
|
||||||
|
import axios from "axios";
|
||||||
|
import Product from "../models/Product";
|
||||||
|
export default {
|
||||||
|
created() {
|
||||||
|
this.getProducts();
|
||||||
|
this.getCategories();
|
||||||
|
},
|
||||||
|
mounted() {
|
||||||
|
const addModal = document.getElementById('editModal');
|
||||||
|
addModal.addEventListener('shown.bs.modal', function () {
|
||||||
|
})
|
||||||
|
},
|
||||||
|
|
||||||
|
data() {
|
||||||
|
return{
|
||||||
|
products: [],
|
||||||
|
categories: [],
|
||||||
|
manufacturers: [],
|
||||||
|
productManufacturers: [],
|
||||||
|
manufacturerId: 0,
|
||||||
|
URL: "http://localhost:8080/",
|
||||||
|
product: new Product(),
|
||||||
|
editedProduct: new Product(),
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
getProducts(){
|
||||||
|
axios.get(this.URL + "product")
|
||||||
|
.then(response => {
|
||||||
|
this.products = response.data;
|
||||||
|
console.log(response.data);
|
||||||
|
})
|
||||||
|
.catch(error => {
|
||||||
|
console.log(error);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
getCategories(){
|
||||||
|
axios.get(this.URL + "category")
|
||||||
|
.then(response => {
|
||||||
|
this.categories = response.data;
|
||||||
|
console.log(response.data);
|
||||||
|
})
|
||||||
|
.catch(error => {
|
||||||
|
console.log(error);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
async addProduct(){
|
||||||
|
await this.toBase64();
|
||||||
|
this.product.price = parseInt(this.product.price);
|
||||||
|
console.log(this.product);
|
||||||
|
axios.post(this.URL + "product", this.product)
|
||||||
|
.then(() => {
|
||||||
|
this.getProducts();
|
||||||
|
this.closeModal();
|
||||||
|
})
|
||||||
|
.catch(error => {
|
||||||
|
console.log(error);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
deleteProduct(id){
|
||||||
|
axios.delete(this.URL + `product/${id}`)
|
||||||
|
.then(() =>{
|
||||||
|
this.getProducts();
|
||||||
|
})
|
||||||
|
},
|
||||||
|
async editProduct(product){
|
||||||
|
if(product.photo === undefined) await this.toBase64();
|
||||||
|
console.log(product);
|
||||||
|
axios.put(this.URL + `product/${product.id}`, product)
|
||||||
|
.then(() =>{
|
||||||
|
const index = this.products.findIndex((s) => s.id === product.id);
|
||||||
|
if (index !== -1) {
|
||||||
|
this.products[index] = { ...product };
|
||||||
|
}
|
||||||
|
this.closeModal();
|
||||||
|
this.getProducts();
|
||||||
|
})
|
||||||
|
this.closeModal();
|
||||||
|
},
|
||||||
|
openModal(status, product = null) {
|
||||||
|
this.getCategories();
|
||||||
|
if (status === "create") {
|
||||||
|
this.product = new Product();
|
||||||
|
this.product.status = "create";
|
||||||
|
} else if (status === "edit" && product) {
|
||||||
|
this.product = { ...product };
|
||||||
|
this.product.status = "edit";
|
||||||
|
}
|
||||||
|
document.getElementById("editModal").style.display = "block";
|
||||||
|
},
|
||||||
|
closeModal() {
|
||||||
|
document.getElementById("editModal").style.display = "none";
|
||||||
|
},
|
||||||
|
async toBase64(){
|
||||||
|
var file = document.getElementById("cover").files[0];
|
||||||
|
var reader = new FileReader();
|
||||||
|
var phototemp = this.product;
|
||||||
|
reader.readAsDataURL(file);
|
||||||
|
await new Promise((resolve, reject) => {
|
||||||
|
reader.onload = function () {
|
||||||
|
phototemp.photo = reader.result;
|
||||||
|
console.log(phototemp);
|
||||||
|
resolve();
|
||||||
|
};
|
||||||
|
reader.onerror = function (error) {
|
||||||
|
console.log('Error: ', error);
|
||||||
|
reject(error);
|
||||||
|
};
|
||||||
|
});
|
||||||
|
},
|
||||||
|
openManyToManyModal(){
|
||||||
|
this.getAllManufacturers();
|
||||||
|
this.getProductManufacturers();
|
||||||
|
document.getElementById("manyToManyModal").style.display = "block";
|
||||||
|
},
|
||||||
|
closeManyToManyModal() {
|
||||||
|
document.getElementById("manyToManyModal").style.display = "none";
|
||||||
|
},
|
||||||
|
getAllManufacturers(){
|
||||||
|
axios.get(this.URL + "manufacturer")
|
||||||
|
.then(response => {
|
||||||
|
this.manufacturers = response.data;
|
||||||
|
console.log(response.data);
|
||||||
|
})
|
||||||
|
.catch(error => {
|
||||||
|
console.log(error);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
getProductManufacturers(){
|
||||||
|
axios.get(this.URL + `product/${this.product.id}/manufacturers`)
|
||||||
|
.then(response => {
|
||||||
|
this.productManufacturers = response.data;
|
||||||
|
console.log(response.data);
|
||||||
|
})
|
||||||
|
.catch(error => {
|
||||||
|
console.log(error);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
addManufacturer(){
|
||||||
|
axios.post(this.URL + `product/${this.product.id}/Manufacturer/${this.manufacturerId}`)
|
||||||
|
.then(() => {
|
||||||
|
this.getProductManufacturers();
|
||||||
|
})
|
||||||
|
.catch(error => {
|
||||||
|
console.log(error);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
removeManufacturer(id){
|
||||||
|
axios.delete(this.URL + `product/${this.product.id}/Manufacturer/${id}`)
|
||||||
|
.then(() =>{
|
||||||
|
this.getProductManufacturers();
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
<style>
|
||||||
|
|
||||||
|
</style>
|
18
frontend/spa-vue/src/routes.js
Normal file
18
frontend/spa-vue/src/routes.js
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
import manufacturers from './pages/manufacturers.vue'
|
||||||
|
import categories from './pages/categories.vue'
|
||||||
|
import products from './pages/products.vue'
|
||||||
|
import {createRouter, createWebHistory} from "vue-router"
|
||||||
|
|
||||||
|
const routes = [
|
||||||
|
{path: '/manufacturer', component: manufacturers},
|
||||||
|
{path: '/category', component: categories},
|
||||||
|
{path: '/product', component: products},
|
||||||
|
]
|
||||||
|
|
||||||
|
const router = createRouter({
|
||||||
|
history: createWebHistory(),
|
||||||
|
linkActiveClass: 'active',
|
||||||
|
routes
|
||||||
|
})
|
||||||
|
|
||||||
|
export default router;
|
42
frontend/spa-vue/src/services/DataService.js
Normal file
42
frontend/spa-vue/src/services/DataService.js
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
import axios from 'axios';
|
||||||
|
|
||||||
|
function toJSON(data) {
|
||||||
|
const jsonObj = {};
|
||||||
|
const fields = Object.getOwnPropertyNames(data);
|
||||||
|
for (const field of fields) {
|
||||||
|
if (data[field] === undefined) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
jsonObj[field.substring(1)] = data[field];
|
||||||
|
}
|
||||||
|
return jsonObj;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default class DataService {
|
||||||
|
static dataUrlPrefix = 'http://localhost:8079/';
|
||||||
|
|
||||||
|
static async readAll(url, transformer) {
|
||||||
|
const response = await axios.get(this.dataUrlPrefix + url);
|
||||||
|
return response.data.map(item => transformer(item));
|
||||||
|
}
|
||||||
|
|
||||||
|
static async read(url, transformer) {
|
||||||
|
const response = await axios.get(this.dataUrlPrefix + url);
|
||||||
|
return transformer(response.data);
|
||||||
|
}
|
||||||
|
|
||||||
|
static async create(url, data) {
|
||||||
|
const response = await axios.post(this.dataUrlPrefix + url, toJSON(data));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static async update(url, data) {
|
||||||
|
const response = await axios.put(this.dataUrlPrefix + url, toJSON(data));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static async delete(url) {
|
||||||
|
const response = await axios.delete(this.dataUrlPrefix + url);
|
||||||
|
return response.data.id;
|
||||||
|
}
|
||||||
|
}
|
89
frontend/spa-vue/src/style.css
Normal file
89
frontend/spa-vue/src/style.css
Normal file
@ -0,0 +1,89 @@
|
|||||||
|
:root {
|
||||||
|
font-family: Inter, system-ui, Avenir, Helvetica, Arial, sans-serif;
|
||||||
|
line-height: 1.5;
|
||||||
|
font-weight: 400;
|
||||||
|
|
||||||
|
color-scheme: light dark;
|
||||||
|
color: rgba(255, 255, 255, 0.87);
|
||||||
|
background-color: #242424;
|
||||||
|
|
||||||
|
font-synthesis: none;
|
||||||
|
text-rendering: optimizeLegibility;
|
||||||
|
-webkit-font-smoothing: antialiased;
|
||||||
|
-moz-osx-font-smoothing: grayscale;
|
||||||
|
-webkit-text-size-adjust: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
a {
|
||||||
|
font-weight: 500;
|
||||||
|
color: #646cff;
|
||||||
|
text-decoration: inherit;
|
||||||
|
}
|
||||||
|
a:hover {
|
||||||
|
color: #535bf2;
|
||||||
|
}
|
||||||
|
|
||||||
|
a {
|
||||||
|
font-weight: 500;
|
||||||
|
color: #646cff;
|
||||||
|
text-decoration: inherit;
|
||||||
|
}
|
||||||
|
a:hover {
|
||||||
|
color: #535bf2;
|
||||||
|
}
|
||||||
|
|
||||||
|
body {
|
||||||
|
margin: 0;
|
||||||
|
display: flex;
|
||||||
|
place-items: center;
|
||||||
|
min-width: 320px;
|
||||||
|
min-height: 100vh;
|
||||||
|
}
|
||||||
|
|
||||||
|
h1 {
|
||||||
|
font-size: 3.2em;
|
||||||
|
line-height: 1.1;
|
||||||
|
}
|
||||||
|
|
||||||
|
button {
|
||||||
|
border-radius: 8px;
|
||||||
|
border: 1px solid transparent;
|
||||||
|
padding: 0.6em 1.2em;
|
||||||
|
font-size: 1em;
|
||||||
|
font-weight: 500;
|
||||||
|
font-family: inherit;
|
||||||
|
background-color: #1a1a1a;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: border-color 0.25s;
|
||||||
|
}
|
||||||
|
button:hover {
|
||||||
|
border-color: #646cff;
|
||||||
|
}
|
||||||
|
button:focus,
|
||||||
|
button:focus-visible {
|
||||||
|
outline: 4px auto -webkit-focus-ring-color;
|
||||||
|
}
|
||||||
|
|
||||||
|
.card {
|
||||||
|
padding: 2em;
|
||||||
|
}
|
||||||
|
|
||||||
|
#app {
|
||||||
|
max-width: 1280px;
|
||||||
|
margin: 0 auto;
|
||||||
|
padding: 2rem;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (prefers-color-scheme: light) {
|
||||||
|
:root {
|
||||||
|
color: #213547;
|
||||||
|
background-color: #ffffff;
|
||||||
|
}
|
||||||
|
a:hover {
|
||||||
|
color: #747bff;
|
||||||
|
}
|
||||||
|
button {
|
||||||
|
background-color: #f9f9f9;
|
||||||
|
}
|
||||||
|
}
|
136
frontend/spa-vue/src/styles.css
Normal file
136
frontend/spa-vue/src/styles.css
Normal file
@ -0,0 +1,136 @@
|
|||||||
|
html {
|
||||||
|
height: 100%;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
body {
|
||||||
|
height: 100%;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
|
||||||
|
nav {
|
||||||
|
font-size: 25px;
|
||||||
|
}
|
||||||
|
|
||||||
|
nav a {
|
||||||
|
color: white;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.navbar-nav .nav-link {
|
||||||
|
color: black;
|
||||||
|
}
|
||||||
|
|
||||||
|
.card {
|
||||||
|
transition: 0.3s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.card:hover {
|
||||||
|
box-shadow: 0px 0px 3px 4px gray;
|
||||||
|
transform: scale(1.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
#brands {
|
||||||
|
margin-top: 5rem;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
#main {
|
||||||
|
margin-top: 5rem;
|
||||||
|
margin-bottom: 5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
#lc_main {
|
||||||
|
padding-top: 2rem;
|
||||||
|
padding-bottom: 2rem;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
#cart_main {
|
||||||
|
margin-top: 2rem;
|
||||||
|
margin-bottom: 2rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
#secondStr {
|
||||||
|
padding-top: 1rem;
|
||||||
|
padding-bottom: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#firstRow {
|
||||||
|
padding: 2rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
#catalog_main .btn {
|
||||||
|
background: orange;
|
||||||
|
border: 2px solid darkgray;
|
||||||
|
}
|
||||||
|
|
||||||
|
#catalog_main .btn:hover {
|
||||||
|
background: lightgreen;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#catalog_main .card {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
padding: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
#catalog_main {
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
#footer_catalog {
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
#information_main {
|
||||||
|
padding-top: 3rem;
|
||||||
|
padding-left: 1rem;
|
||||||
|
padding-right: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
#information_main {
|
||||||
|
font-size: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#information_main .card {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
#main {
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
#registrationForm {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
#submitBtn {
|
||||||
|
margin-left: auto;
|
||||||
|
margin-right: auto;
|
||||||
|
max-width: 200px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.input.error {
|
||||||
|
border-color: red;
|
||||||
|
}
|
||||||
|
|
||||||
|
.checkbox__input.error + .checkbox__label {
|
||||||
|
color: red;
|
||||||
|
}
|
||||||
|
|
||||||
|
#frm-items {
|
||||||
|
padding: 32px;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tbl {
|
||||||
|
padding-left: 32px;
|
||||||
|
padding-right: 32px;
|
||||||
|
}
|
7
frontend/spa-vue/vite.config.js
Normal file
7
frontend/spa-vue/vite.config.js
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
import { defineConfig } from 'vite'
|
||||||
|
import vue from '@vitejs/plugin-vue'
|
||||||
|
|
||||||
|
// https://vitejs.dev/config/
|
||||||
|
export default defineConfig({
|
||||||
|
plugins: [vue()],
|
||||||
|
})
|
@ -0,0 +1,49 @@
|
|||||||
|
package ru.ulstu.is.sbapp.HardwareShop.controller;
|
||||||
|
|
||||||
|
import org.springframework.web.bind.annotation.*;
|
||||||
|
import ru.ulstu.is.sbapp.HardwareShop.models.Category;
|
||||||
|
import ru.ulstu.is.sbapp.HardwareShop.services.CategoryService;
|
||||||
|
import ru.ulstu.is.sbapp.WebConfiguration;
|
||||||
|
|
||||||
|
import javax.validation.Valid;
|
||||||
|
import javax.xml.crypto.dsig.CanonicalizationMethod;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
@RestController
|
||||||
|
@RequestMapping(WebConfiguration.REST_API + "/category")
|
||||||
|
public class CategoryController {
|
||||||
|
private final CategoryService categoryService;
|
||||||
|
|
||||||
|
public CategoryController(CategoryService categoryService) {
|
||||||
|
this.categoryService = categoryService;
|
||||||
|
}
|
||||||
|
|
||||||
|
@GetMapping("/{id}")
|
||||||
|
public CategoryDTO getCategory(@PathVariable Long id) {
|
||||||
|
return new CategoryDTO(categoryService.findCategory(id));
|
||||||
|
}
|
||||||
|
|
||||||
|
@GetMapping
|
||||||
|
public List<CategoryDTO> getCategories() {
|
||||||
|
return categoryService.findAllCategories().stream()
|
||||||
|
.map(CategoryDTO::new)
|
||||||
|
.toList();
|
||||||
|
}
|
||||||
|
|
||||||
|
@PostMapping
|
||||||
|
public CategoryDTO createCategory(@RequestBody @Valid CategoryDTO categoryDTO) throws IOException {
|
||||||
|
return new CategoryDTO(categoryService.addCategory(categoryDTO));
|
||||||
|
}
|
||||||
|
|
||||||
|
@PutMapping("/{id}")
|
||||||
|
public CategoryDTO updateCategory(@PathVariable Long id, @RequestBody @Valid CategoryDTO categoryDTO) {
|
||||||
|
return new CategoryDTO(categoryService.updateCategory(id, categoryDTO));
|
||||||
|
}
|
||||||
|
|
||||||
|
@DeleteMapping("/{id}")
|
||||||
|
public CategoryDTO deleteCategory(@PathVariable Long id) {
|
||||||
|
return new CategoryDTO(categoryService.deleteCategory(id));
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,31 @@
|
|||||||
|
package ru.ulstu.is.sbapp.HardwareShop.controller;
|
||||||
|
|
||||||
|
import ru.ulstu.is.sbapp.HardwareShop.models.Category;
|
||||||
|
import ru.ulstu.is.sbapp.HardwareShop.models.Product;
|
||||||
|
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
|
|
||||||
|
public class CategoryDTO {
|
||||||
|
private Long id;
|
||||||
|
|
||||||
|
private String name;
|
||||||
|
|
||||||
|
public CategoryDTO() {}
|
||||||
|
|
||||||
|
public CategoryDTO(Category category) {
|
||||||
|
this.id = category.getId();
|
||||||
|
this.name = category.getName();
|
||||||
|
}
|
||||||
|
|
||||||
|
public Long getId() {
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getName() {
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setId(Long id) { this.id = id; }
|
||||||
|
|
||||||
|
public void setName(String name) { this.name = name; }
|
||||||
|
}
|
@ -0,0 +1,65 @@
|
|||||||
|
package ru.ulstu.is.sbapp.HardwareShop.controller;
|
||||||
|
|
||||||
|
import org.springframework.stereotype.Controller;
|
||||||
|
import org.springframework.ui.Model;
|
||||||
|
import org.springframework.validation.BindingResult;
|
||||||
|
import org.springframework.web.bind.annotation.*;
|
||||||
|
import ru.ulstu.is.sbapp.HardwareShop.models.Category;
|
||||||
|
import ru.ulstu.is.sbapp.HardwareShop.services.CategoryService;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
@Controller
|
||||||
|
@RequestMapping("/category")
|
||||||
|
public class CategoryMvcController {
|
||||||
|
private final CategoryService categoryService;
|
||||||
|
|
||||||
|
public CategoryMvcController(CategoryService categoryService) {
|
||||||
|
this.categoryService = categoryService;
|
||||||
|
}
|
||||||
|
|
||||||
|
@GetMapping
|
||||||
|
public String getCategories(Model model) {
|
||||||
|
model.addAttribute("categories", categoryService.findAllCategories()
|
||||||
|
.stream()
|
||||||
|
.map(CategoryDTO::new)
|
||||||
|
.toList());
|
||||||
|
return "categories";
|
||||||
|
}
|
||||||
|
|
||||||
|
@GetMapping(value = {"/update", "/update/{id}"})
|
||||||
|
public String updateCategory(@PathVariable(required = false) Long id,
|
||||||
|
Model model) {
|
||||||
|
if (id == null || id <= 0) {
|
||||||
|
model.addAttribute("categoryDto", new CategoryDTO());
|
||||||
|
} else {
|
||||||
|
model.addAttribute("categoryDto", id);
|
||||||
|
model.addAttribute("categoryDto", new CategoryDTO(categoryService.findCategory(id)));
|
||||||
|
}
|
||||||
|
return "category-update";
|
||||||
|
}
|
||||||
|
|
||||||
|
@PostMapping(value = {"/", "/{id}"})
|
||||||
|
public String saveCategory(@PathVariable(required = false) Long id,
|
||||||
|
@ModelAttribute("categoryDto") CategoryDTO categoryDTO,
|
||||||
|
BindingResult bindingResult,
|
||||||
|
Model model) throws IOException {
|
||||||
|
if (bindingResult.hasErrors()) {
|
||||||
|
model.addAttribute("errors",
|
||||||
|
bindingResult.getAllErrors());
|
||||||
|
return "category-update";
|
||||||
|
}
|
||||||
|
if (id == null || id <= 0) {
|
||||||
|
categoryService.addCategory(categoryDTO);
|
||||||
|
} else {
|
||||||
|
categoryService.updateCategory(id, categoryDTO);
|
||||||
|
}
|
||||||
|
return "redirect:/category";
|
||||||
|
}
|
||||||
|
|
||||||
|
@PostMapping("/delete/{id}")
|
||||||
|
public String deleteCategory(@PathVariable Long id) {
|
||||||
|
categoryService.deleteCategory(id);
|
||||||
|
return "redirect:/category";
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,48 @@
|
|||||||
|
package ru.ulstu.is.sbapp.HardwareShop.controller;
|
||||||
|
|
||||||
|
|
||||||
|
import org.springframework.boot.web.servlet.ServletComponentScan;
|
||||||
|
import org.springframework.web.bind.annotation.*;
|
||||||
|
import ru.ulstu.is.sbapp.HardwareShop.services.ManufacturerService;
|
||||||
|
import ru.ulstu.is.sbapp.WebConfiguration;
|
||||||
|
|
||||||
|
import javax.validation.Valid;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
@RestController
|
||||||
|
@RequestMapping(WebConfiguration.REST_API + "/manufacturer")
|
||||||
|
public class ManufacturerController {
|
||||||
|
private final ManufacturerService manufacturerService;
|
||||||
|
|
||||||
|
public ManufacturerController(ManufacturerService manufacturerService) {
|
||||||
|
this.manufacturerService = manufacturerService;
|
||||||
|
}
|
||||||
|
|
||||||
|
@GetMapping("/{id}")
|
||||||
|
public ManufacturerDTO getManufacturer(@PathVariable Long id) {
|
||||||
|
return new ManufacturerDTO(manufacturerService.findManufacturer(id));
|
||||||
|
}
|
||||||
|
|
||||||
|
@GetMapping()
|
||||||
|
public List<ManufacturerDTO> getManufacturers() {
|
||||||
|
return manufacturerService.findAllManufacturers().stream()
|
||||||
|
.map(ManufacturerDTO::new)
|
||||||
|
.toList();
|
||||||
|
}
|
||||||
|
|
||||||
|
@PostMapping
|
||||||
|
public ManufacturerDTO createManufacturer(@RequestBody @Valid ManufacturerDTO manufacturerDTO) throws IOException {
|
||||||
|
return new ManufacturerDTO(manufacturerService.addManufacturer(manufacturerDTO));
|
||||||
|
}
|
||||||
|
|
||||||
|
@PutMapping("/{id}")
|
||||||
|
public ManufacturerDTO updateManufacturer(@PathVariable Long id, @RequestBody @Valid ManufacturerDTO manufacturerDTO) throws IOException {
|
||||||
|
return new ManufacturerDTO(manufacturerService.updateManufacturer(id, manufacturerDTO));
|
||||||
|
}
|
||||||
|
|
||||||
|
@DeleteMapping("/{id}")
|
||||||
|
public ManufacturerDTO deleteManufacturer(@PathVariable Long id) {
|
||||||
|
return new ManufacturerDTO(manufacturerService.deleteManufacturer(id));
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,39 @@
|
|||||||
|
package ru.ulstu.is.sbapp.HardwareShop.controller;
|
||||||
|
|
||||||
|
import ru.ulstu.is.sbapp.HardwareShop.models.Manufacturer;
|
||||||
|
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
|
|
||||||
|
public class ManufacturerDTO {
|
||||||
|
private Long id;
|
||||||
|
|
||||||
|
private String name;
|
||||||
|
|
||||||
|
private String address;
|
||||||
|
|
||||||
|
public ManufacturerDTO() {}
|
||||||
|
|
||||||
|
public ManufacturerDTO(Manufacturer manufacturer) {
|
||||||
|
this.id = manufacturer.getId();
|
||||||
|
this.name = manufacturer.getName();
|
||||||
|
this.address = manufacturer.getAddress();
|
||||||
|
}
|
||||||
|
|
||||||
|
public Long getId() {
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getName() {
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getAddress() {
|
||||||
|
return address;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setId(Long id) { this.id = id; }
|
||||||
|
|
||||||
|
public void setName(String name) { this.name = name; }
|
||||||
|
|
||||||
|
public void setAddress(String address) { this.address = address; }
|
||||||
|
}
|
@ -0,0 +1,64 @@
|
|||||||
|
package ru.ulstu.is.sbapp.HardwareShop.controller;
|
||||||
|
|
||||||
|
import org.springframework.stereotype.Controller;
|
||||||
|
import org.springframework.ui.Model;
|
||||||
|
import org.springframework.validation.BindingResult;
|
||||||
|
import org.springframework.web.bind.annotation.*;
|
||||||
|
import ru.ulstu.is.sbapp.HardwareShop.services.ManufacturerService;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
@Controller
|
||||||
|
@RequestMapping("/manufacturer")
|
||||||
|
public class ManufacturerMvcController {
|
||||||
|
private final ManufacturerService manufacturerService;
|
||||||
|
|
||||||
|
public ManufacturerMvcController(ManufacturerService manufacturerService) {
|
||||||
|
this.manufacturerService = manufacturerService;
|
||||||
|
}
|
||||||
|
|
||||||
|
@GetMapping
|
||||||
|
public String getManufacturers(Model model) {
|
||||||
|
model.addAttribute("manufacturers",
|
||||||
|
manufacturerService.findAllManufacturers().stream()
|
||||||
|
.map(ManufacturerDTO::new)
|
||||||
|
.toList());
|
||||||
|
return "manufacturers";
|
||||||
|
}
|
||||||
|
|
||||||
|
@GetMapping(value = {"/update", "/update/{id}"})
|
||||||
|
public String editManufacturer(@PathVariable(required = false) Long id,
|
||||||
|
Model model) {
|
||||||
|
if (id == null || id <= 0) {
|
||||||
|
model.addAttribute("manufacturerDto", new ManufacturerDTO());
|
||||||
|
} else {
|
||||||
|
model.addAttribute("manufacturerDto", id);
|
||||||
|
model.addAttribute("manufacturerDto", new ManufacturerDTO(manufacturerService.findManufacturer(id)));
|
||||||
|
}
|
||||||
|
return "manufacturer-update";
|
||||||
|
}
|
||||||
|
|
||||||
|
@PostMapping(value = {"/", "/{id}"})
|
||||||
|
public String saveManufacturer(@PathVariable(required = false) Long id,
|
||||||
|
@ModelAttribute("manufacturerDto") ManufacturerDTO manufacturerDTO,
|
||||||
|
BindingResult bindingResult,
|
||||||
|
Model model) throws IOException {
|
||||||
|
if (bindingResult.hasErrors()) {
|
||||||
|
model.addAttribute("errors",
|
||||||
|
bindingResult.getAllErrors());
|
||||||
|
return "manufacturer-update";
|
||||||
|
}
|
||||||
|
if (id == null || id <= 0) {
|
||||||
|
manufacturerService.addManufacturer(manufacturerDTO);
|
||||||
|
} else {
|
||||||
|
manufacturerService.updateManufacturer(id, manufacturerDTO);
|
||||||
|
}
|
||||||
|
return "redirect:/manufacturer";
|
||||||
|
}
|
||||||
|
|
||||||
|
@PostMapping("/delete/{id}")
|
||||||
|
public String deleteGenre(@PathVariable Long id) {
|
||||||
|
manufacturerService.deleteManufacturer(id);
|
||||||
|
return "redirect:/manufacturer";
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,72 @@
|
|||||||
|
package ru.ulstu.is.sbapp.HardwareShop.controller;
|
||||||
|
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.web.bind.annotation.*;
|
||||||
|
import org.springframework.web.multipart.MultipartFile;
|
||||||
|
import ru.ulstu.is.sbapp.HardwareShop.models.Product;
|
||||||
|
import ru.ulstu.is.sbapp.HardwareShop.services.ManufacturerService;
|
||||||
|
import ru.ulstu.is.sbapp.HardwareShop.services.ProductService;
|
||||||
|
import ru.ulstu.is.sbapp.WebConfiguration;
|
||||||
|
|
||||||
|
import javax.validation.Valid;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.Base64;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
@RestController
|
||||||
|
@RequestMapping(WebConfiguration.REST_API + "/product")
|
||||||
|
public class ProductController {
|
||||||
|
|
||||||
|
private final ProductService productService;
|
||||||
|
|
||||||
|
public ProductController(ProductService productService) {
|
||||||
|
this.productService = productService;
|
||||||
|
}
|
||||||
|
|
||||||
|
@GetMapping("/{id}")
|
||||||
|
public ProductDTO getProduct(@PathVariable Long id) {
|
||||||
|
return new ProductDTO(productService.findProduct(id));
|
||||||
|
}
|
||||||
|
|
||||||
|
@GetMapping
|
||||||
|
public List<ProductDTO> getProducts() {
|
||||||
|
return productService.findAllProducts().stream()
|
||||||
|
.map(ProductDTO::new)
|
||||||
|
.toList();
|
||||||
|
}
|
||||||
|
|
||||||
|
@GetMapping("/{id}/manufacturers")
|
||||||
|
public List<ManufacturerDTO> getProductManufacturers(@PathVariable Long id){
|
||||||
|
return productService.getProductManufacturers(id).stream().map(ManufacturerDTO::new).toList();
|
||||||
|
}
|
||||||
|
|
||||||
|
@PostMapping
|
||||||
|
public ProductDTO createProduct(@RequestBody @Valid ProductDTO productDTO) throws IOException{
|
||||||
|
return new ProductDTO(productService.addProduct(productDTO));
|
||||||
|
}
|
||||||
|
|
||||||
|
@PutMapping("/{id}")
|
||||||
|
public ProductDTO updateProduct(@PathVariable Long id, @RequestBody @Valid ProductDTO productDTO) {
|
||||||
|
return new ProductDTO(productService.updateProduct(id, productDTO));
|
||||||
|
}
|
||||||
|
|
||||||
|
@PostMapping("/{id}/Manufacturer/{manufacturerId}")
|
||||||
|
public void addManufacturer(@PathVariable Long id, @PathVariable Long manufacturerId) {
|
||||||
|
productService.addManufacturersToProduct(id, manufacturerId);
|
||||||
|
}
|
||||||
|
|
||||||
|
@DeleteMapping("/{id}/Manufacturer/{manufacturerId}")
|
||||||
|
public void removeManufacturer(@PathVariable Long id, @PathVariable Long manufacturerId) {
|
||||||
|
productService.removeManufacturersToProduct(id, manufacturerId);
|
||||||
|
}
|
||||||
|
|
||||||
|
@DeleteMapping("/{id}")
|
||||||
|
public ProductDTO deleteProduct(@PathVariable Long id) {
|
||||||
|
return new ProductDTO(productService.deleteProduct(id));
|
||||||
|
}
|
||||||
|
|
||||||
|
@DeleteMapping
|
||||||
|
public void deleteAll() {
|
||||||
|
productService.deleteAllProducts();
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,64 @@
|
|||||||
|
package ru.ulstu.is.sbapp.HardwareShop.controller;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||||
|
import ru.ulstu.is.sbapp.HardwareShop.models.Product;
|
||||||
|
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
|
|
||||||
|
public class ProductDTO {
|
||||||
|
private Long id;
|
||||||
|
|
||||||
|
private Integer price;
|
||||||
|
|
||||||
|
private String photo;
|
||||||
|
|
||||||
|
@JsonProperty("categoryId")
|
||||||
|
private Long category_id;
|
||||||
|
|
||||||
|
private String categoryName;
|
||||||
|
|
||||||
|
private String name;
|
||||||
|
|
||||||
|
public ProductDTO() {}
|
||||||
|
|
||||||
|
public ProductDTO(Product product) {
|
||||||
|
this.id = product.getId();
|
||||||
|
this.name = product.getName();
|
||||||
|
this.price = product.getPrice();
|
||||||
|
this.photo = new String(product.getPhoto(), StandardCharsets.UTF_8);
|
||||||
|
this.category_id = product.getCategory().getId();
|
||||||
|
this.categoryName = product.getCategory().getName();
|
||||||
|
}
|
||||||
|
|
||||||
|
public Long getId() {
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getName() { return name; }
|
||||||
|
|
||||||
|
public Integer getPrice() {
|
||||||
|
return price;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getPhoto() {
|
||||||
|
return photo;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Long getCategory_id() {
|
||||||
|
return category_id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getCategoryName() { return categoryName; }
|
||||||
|
|
||||||
|
public void setId(Long id) { this.id = id; }
|
||||||
|
|
||||||
|
public void setPhoto(String photo) { this.photo = photo; }
|
||||||
|
|
||||||
|
public void setName(String name) { this.name = name; }
|
||||||
|
|
||||||
|
public void setPrice(Integer price) { this.price = price; }
|
||||||
|
|
||||||
|
public void setCategoryName(String name) { this.categoryName = name; }
|
||||||
|
|
||||||
|
public void setCategory_id(Long categoryId) {this.category_id = categoryId;}
|
||||||
|
}
|
@ -0,0 +1,135 @@
|
|||||||
|
package ru.ulstu.is.sbapp.HardwareShop.controller;
|
||||||
|
|
||||||
|
import org.springframework.stereotype.Controller;
|
||||||
|
import org.springframework.ui.Model;
|
||||||
|
import org.springframework.validation.BindingResult;
|
||||||
|
import org.springframework.web.bind.annotation.*;
|
||||||
|
import org.springframework.web.multipart.MultipartFile;
|
||||||
|
import ru.ulstu.is.sbapp.HardwareShop.models.Product;
|
||||||
|
import ru.ulstu.is.sbapp.HardwareShop.services.CategoryService;
|
||||||
|
import ru.ulstu.is.sbapp.HardwareShop.services.ManufacturerService;
|
||||||
|
import ru.ulstu.is.sbapp.HardwareShop.services.ProductService;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Base64;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
@Controller
|
||||||
|
@RequestMapping("/product")
|
||||||
|
public class ProductMvcController {
|
||||||
|
private final ProductService productService;
|
||||||
|
private final ManufacturerService manufacturerService;
|
||||||
|
|
||||||
|
private final CategoryService categoryService;
|
||||||
|
|
||||||
|
public ProductMvcController(ProductService productService, ManufacturerService manufacturerService, CategoryService categoryService) {
|
||||||
|
this.productService = productService;
|
||||||
|
this.manufacturerService = manufacturerService;
|
||||||
|
this.categoryService = categoryService;
|
||||||
|
}
|
||||||
|
|
||||||
|
@GetMapping
|
||||||
|
public String getProducts(Model model, @RequestParam(required = false)List<ManufacturerDTO> manufacturerDTOS) {
|
||||||
|
List<ProductDTO> productDTOS;
|
||||||
|
if (manufacturerDTOS == null) {
|
||||||
|
productDTOS = productService.findAllProducts().stream()
|
||||||
|
.map(ProductDTO::new)
|
||||||
|
.toList();
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
productDTOS = productService.getProductsFilter(manufacturerDTOS)
|
||||||
|
.stream()
|
||||||
|
.map(ProductDTO::new)
|
||||||
|
.toList();
|
||||||
|
}
|
||||||
|
model.addAttribute("products", productDTOS);
|
||||||
|
return "products";
|
||||||
|
}
|
||||||
|
|
||||||
|
@GetMapping("filter")
|
||||||
|
public String FilterMethod(@RequestParam(required = false) Long manufacturerid, Model model, @RequestParam(name = "selected", required = false)List<ManufacturerDTO> selected) {
|
||||||
|
if (manufacturerid != null && manufacturerid != 0) {
|
||||||
|
if (selected == null) {
|
||||||
|
selected = new ArrayList<>();
|
||||||
|
}
|
||||||
|
selected.add(new ManufacturerDTO(manufacturerService.findManufacturer(manufacturerid)));
|
||||||
|
}
|
||||||
|
model.addAttribute("selected", selected);
|
||||||
|
model.addAttribute("manufacturers", manufacturerService.findAllManufacturers()
|
||||||
|
.stream()
|
||||||
|
.map(ManufacturerDTO::new)
|
||||||
|
.toList());
|
||||||
|
return "product-filter";
|
||||||
|
}
|
||||||
|
|
||||||
|
@GetMapping(value = {"/update", "/update/{id}"})
|
||||||
|
public String updateProduct(@PathVariable(required = false) Long id,
|
||||||
|
Model model) {
|
||||||
|
model.addAttribute("categories",categoryService.findAllCategories());
|
||||||
|
model.addAttribute("Manufacturers", manufacturerService.findAllManufacturers());
|
||||||
|
if (id == null || id <= 0) {
|
||||||
|
model.addAttribute("productDto", new ProductDTO());
|
||||||
|
} else {
|
||||||
|
model.addAttribute("productDto", id);
|
||||||
|
model.addAttribute("productDto", new ProductDTO(productService.findProduct(id)));
|
||||||
|
}
|
||||||
|
return "product-update";
|
||||||
|
}
|
||||||
|
|
||||||
|
@PostMapping(value = {"/", "/{id}"})
|
||||||
|
public String saveProduct(@PathVariable(required = false) Long id,
|
||||||
|
@RequestParam(value = "multipartFile") MultipartFile multipartFile,
|
||||||
|
@RequestParam(value = "categoryId") Long category_id,
|
||||||
|
@ModelAttribute("productDto") ProductDTO productDTO,
|
||||||
|
BindingResult bindingResult,
|
||||||
|
Model model) throws IOException {
|
||||||
|
if (bindingResult.hasErrors()) {
|
||||||
|
model.addAttribute("errors",
|
||||||
|
bindingResult.getAllErrors());
|
||||||
|
return "product-update";
|
||||||
|
}
|
||||||
|
productDTO.setPhoto("data:" + multipartFile.getContentType() + ";base64," + Base64.getEncoder().encodeToString(multipartFile.getBytes()));
|
||||||
|
productDTO.setCategory_id(category_id);
|
||||||
|
productDTO.setCategoryName(categoryService.findCategory(productDTO.getCategory_id()).getName());
|
||||||
|
if (id == null || id <= 0) {
|
||||||
|
return "redirect:/product/" + productService.addProduct(productDTO).getId().toString() + "/manufacturers";
|
||||||
|
} else {
|
||||||
|
productService.updateProduct(id, productDTO);
|
||||||
|
}
|
||||||
|
return "redirect:/product";
|
||||||
|
}
|
||||||
|
|
||||||
|
@PostMapping("/delete/{id}")
|
||||||
|
public String deleteBook(@PathVariable Long id) {
|
||||||
|
productService.deleteProduct(id);
|
||||||
|
return "redirect:/product";
|
||||||
|
}
|
||||||
|
|
||||||
|
@GetMapping("/{id}/manufacturers")
|
||||||
|
public String getProductManufacturers(@PathVariable Long id, Model model){
|
||||||
|
model.addAttribute("product",
|
||||||
|
new ProductDTO(productService.findProduct(id)));
|
||||||
|
model.addAttribute("productmanufacturers",
|
||||||
|
productService.getProductManufacturers(id).stream()
|
||||||
|
.map(ManufacturerDTO::new)
|
||||||
|
.toList());
|
||||||
|
model.addAttribute("manufacturers",
|
||||||
|
manufacturerService.findAllManufacturers().stream()
|
||||||
|
.map(ManufacturerDTO::new)
|
||||||
|
.toList());
|
||||||
|
return "product-mtm";
|
||||||
|
}
|
||||||
|
|
||||||
|
@PostMapping("/{id}/manufacturers")
|
||||||
|
public String addManufacturerToProduct(@PathVariable Long id, @RequestParam(value = "manufacturerid") Long manufacturerid){
|
||||||
|
productService.addManufacturersToProduct(id, manufacturerid);
|
||||||
|
return "redirect:/product/" + id.toString() + "/manufacturers";
|
||||||
|
}
|
||||||
|
|
||||||
|
@PostMapping("/{id}/manufacturers/{manufacturerid}")
|
||||||
|
public String removeManufacturerFromProduct(@PathVariable Long id, @PathVariable Long manufacturerid){
|
||||||
|
productService.removeManufacturersToProduct(id, manufacturerid);
|
||||||
|
return "redirect:/product/" + id.toString() + "/manufacturers";
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,28 @@
|
|||||||
|
package ru.ulstu.is.sbapp.HardwareShop.controller;
|
||||||
|
|
||||||
|
import ru.ulstu.is.sbapp.HardwareShop.models.User;
|
||||||
|
import ru.ulstu.is.sbapp.HardwareShop.models.UserRole;
|
||||||
|
|
||||||
|
public class UserDto {
|
||||||
|
private final long id;
|
||||||
|
private final String login;
|
||||||
|
private final UserRole role;
|
||||||
|
|
||||||
|
public UserDto(User user) {
|
||||||
|
this.id = user.getId();
|
||||||
|
this.login = user.getLogin();
|
||||||
|
this.role = user.getRole();
|
||||||
|
}
|
||||||
|
|
||||||
|
public long getId() {
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getLogin() {
|
||||||
|
return login;
|
||||||
|
}
|
||||||
|
|
||||||
|
public UserRole getRole() {
|
||||||
|
return role;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,42 @@
|
|||||||
|
package ru.ulstu.is.sbapp.HardwareShop.controller;
|
||||||
|
|
||||||
|
import org.springframework.data.domain.Page;
|
||||||
|
import org.springframework.security.access.annotation.Secured;
|
||||||
|
import org.springframework.stereotype.Controller;
|
||||||
|
import org.springframework.ui.Model;
|
||||||
|
import org.springframework.web.bind.annotation.GetMapping;
|
||||||
|
import org.springframework.web.bind.annotation.RequestMapping;
|
||||||
|
import org.springframework.web.bind.annotation.RequestParam;
|
||||||
|
import org.springframework.web.bind.annotation.RestController;
|
||||||
|
import ru.ulstu.is.sbapp.HardwareShop.models.UserRole;
|
||||||
|
import ru.ulstu.is.sbapp.HardwareShop.services.UserService;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.stream.IntStream;
|
||||||
|
|
||||||
|
@RestController
|
||||||
|
@RequestMapping("/users")
|
||||||
|
public class UserMvcController {
|
||||||
|
private final UserService userService;
|
||||||
|
|
||||||
|
public UserMvcController(UserService userService) {
|
||||||
|
this.userService = userService;
|
||||||
|
}
|
||||||
|
|
||||||
|
@GetMapping
|
||||||
|
@Secured({UserRole.AsString.ADMIN})
|
||||||
|
public String getUsers(@RequestParam(defaultValue = "1") int page,
|
||||||
|
@RequestParam(defaultValue = "5") int size,
|
||||||
|
Model model) {
|
||||||
|
final Page<UserDto> users = userService.findAllPages(page, size)
|
||||||
|
.map(UserDto::new);
|
||||||
|
model.addAttribute("users", users);
|
||||||
|
final int totalPages = users.getTotalPages();
|
||||||
|
final List<Integer> pageNumbers = IntStream.rangeClosed(1, totalPages)
|
||||||
|
.boxed()
|
||||||
|
.toList();
|
||||||
|
model.addAttribute("pages", pageNumbers);
|
||||||
|
model.addAttribute("totalPages", totalPages);
|
||||||
|
return "users";
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,40 @@
|
|||||||
|
package ru.ulstu.is.sbapp.HardwareShop.controller;
|
||||||
|
|
||||||
|
import jakarta.validation.constraints.NotBlank;
|
||||||
|
import jakarta.validation.constraints.Size;
|
||||||
|
|
||||||
|
public class UserSignUpDTO {
|
||||||
|
@NotBlank
|
||||||
|
@Size(min = 3, max = 64)
|
||||||
|
private String login;
|
||||||
|
@NotBlank
|
||||||
|
@Size(min = 6, max = 64)
|
||||||
|
private String password;
|
||||||
|
@NotBlank
|
||||||
|
@Size(min = 6, max = 64)
|
||||||
|
private String passwordConfirm;
|
||||||
|
|
||||||
|
public String getLogin() {
|
||||||
|
return login;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setLogin(String login) {
|
||||||
|
this.login = login;
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,48 @@
|
|||||||
|
package ru.ulstu.is.sbapp.HardwareShop.controller;
|
||||||
|
|
||||||
|
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 ru.ulstu.is.sbapp.HardwareShop.models.User;
|
||||||
|
import ru.ulstu.is.sbapp.HardwareShop.services.UserService;
|
||||||
|
|
||||||
|
import jakarta.validation.Valid;
|
||||||
|
import jakarta.validation.ValidationException;
|
||||||
|
|
||||||
|
@Controller
|
||||||
|
@RequestMapping(UserSignUpMvcController.SIGNUP_URL)
|
||||||
|
public class UserSignUpMvcController {
|
||||||
|
public static final String SIGNUP_URL = "/signup";
|
||||||
|
private final UserService userService;
|
||||||
|
|
||||||
|
public UserSignUpMvcController(UserService userService) {
|
||||||
|
this.userService = userService;
|
||||||
|
}
|
||||||
|
|
||||||
|
@GetMapping
|
||||||
|
public String showSignupForm(Model model) {
|
||||||
|
model.addAttribute("UserDTO", new UserSignUpDTO());
|
||||||
|
return "signup";
|
||||||
|
}
|
||||||
|
|
||||||
|
@PostMapping
|
||||||
|
public String signup(@ModelAttribute("UserDTO") @Valid UserSignUpDTO userSignupDto,
|
||||||
|
BindingResult bindingResult,
|
||||||
|
Model model) {
|
||||||
|
if (bindingResult.hasErrors()) {
|
||||||
|
model.addAttribute("errors", bindingResult.getAllErrors());
|
||||||
|
return "signup";
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
final User user = userService.createUser(userSignupDto.getLogin(), userSignupDto.getPassword(), userSignupDto.getPasswordConfirm());
|
||||||
|
return "redirect:/login?created=" + user.getLogin();
|
||||||
|
} catch (ValidationException e) {
|
||||||
|
model.addAttribute("errors", e.getMessage());
|
||||||
|
return "signup";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,57 @@
|
|||||||
|
package ru.ulstu.is.sbapp.HardwareShop.models;
|
||||||
|
|
||||||
|
import ru.ulstu.is.sbapp.HardwareShop.controller.CategoryDTO;
|
||||||
|
import ru.ulstu.is.sbapp.HardwareShop.controller.ProductDTO;
|
||||||
|
|
||||||
|
import javax.persistence.*;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Objects;
|
||||||
|
|
||||||
|
@Entity
|
||||||
|
public class Category {
|
||||||
|
@Id
|
||||||
|
@GeneratedValue(strategy = GenerationType.AUTO)
|
||||||
|
private Long id;
|
||||||
|
|
||||||
|
@Column
|
||||||
|
private String name;
|
||||||
|
|
||||||
|
public Category() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public Category(String name) {
|
||||||
|
this.name = name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Category(CategoryDTO categoryDTO){
|
||||||
|
this.name = categoryDTO.getName();
|
||||||
|
}
|
||||||
|
|
||||||
|
public Long getId() { return id;}
|
||||||
|
|
||||||
|
public String getName() { return name; }
|
||||||
|
|
||||||
|
public void setName(String name) { this.name = name; }
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object o) {
|
||||||
|
if (this == o) return true;
|
||||||
|
if (o == null || getClass() != o.getClass()) return false;
|
||||||
|
Category category = (Category) o;
|
||||||
|
return Objects.equals(id, category.id);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
return Objects.hash(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "Category{" +
|
||||||
|
"id=" + id +
|
||||||
|
", name='" + name + '\'' +
|
||||||
|
'}';
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,71 @@
|
|||||||
|
package ru.ulstu.is.sbapp.HardwareShop.models;
|
||||||
|
|
||||||
|
import ru.ulstu.is.sbapp.HardwareShop.controller.CategoryDTO;
|
||||||
|
import ru.ulstu.is.sbapp.HardwareShop.controller.ManufacturerDTO;
|
||||||
|
|
||||||
|
import javax.persistence.*;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Objects;
|
||||||
|
|
||||||
|
@Entity
|
||||||
|
public class Manufacturer {
|
||||||
|
@Id
|
||||||
|
@GeneratedValue(strategy = GenerationType.AUTO)
|
||||||
|
private Long id;
|
||||||
|
|
||||||
|
@ManyToMany(fetch = FetchType.EAGER,
|
||||||
|
mappedBy = "manufacturerList")
|
||||||
|
private List<Product> productList;
|
||||||
|
|
||||||
|
@Column
|
||||||
|
private String name;
|
||||||
|
private String address;
|
||||||
|
|
||||||
|
|
||||||
|
public Manufacturer() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public Manufacturer(String name, String address) {
|
||||||
|
this.name = name;
|
||||||
|
this.address = address;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Manufacturer(ManufacturerDTO manufacturerDTO){
|
||||||
|
this.name = manufacturerDTO.getName();
|
||||||
|
this.address = manufacturerDTO.getAddress();
|
||||||
|
}
|
||||||
|
|
||||||
|
public Long getId() { return id;}
|
||||||
|
|
||||||
|
public String getName() { return name; }
|
||||||
|
|
||||||
|
public String getAddress() { return address; }
|
||||||
|
|
||||||
|
public List<Product> getProductList() { return productList; }
|
||||||
|
public void setName(String name) { this.name = name; }
|
||||||
|
|
||||||
|
public void setAddress(String address) { this.address = address; }
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object o) {
|
||||||
|
if (this == o) return true;
|
||||||
|
if (o == null || getClass() != o.getClass()) return false;
|
||||||
|
Manufacturer manufacturer = (Manufacturer) o;
|
||||||
|
return Objects.equals(id, manufacturer.id);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
return Objects.hash(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "Manufacturer{" +
|
||||||
|
"id=" + id +
|
||||||
|
", name='" + name + '\'' +
|
||||||
|
", address='" + address + '\'' +
|
||||||
|
'}';
|
||||||
|
}
|
||||||
|
}
|
102
src/main/java/ru/ulstu/is/sbapp/HardwareShop/models/Product.java
Normal file
102
src/main/java/ru/ulstu/is/sbapp/HardwareShop/models/Product.java
Normal file
@ -0,0 +1,102 @@
|
|||||||
|
package ru.ulstu.is.sbapp.HardwareShop.models;
|
||||||
|
|
||||||
|
import ru.ulstu.is.sbapp.HardwareShop.controller.ProductDTO;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Objects;
|
||||||
|
import javax.persistence.*;
|
||||||
|
import javax.validation.groups.ConvertGroup;
|
||||||
|
|
||||||
|
@Entity
|
||||||
|
public class Product {
|
||||||
|
@Id
|
||||||
|
@GeneratedValue(strategy = GenerationType.AUTO)
|
||||||
|
private Long id;
|
||||||
|
|
||||||
|
@ManyToOne(fetch = FetchType.LAZY)
|
||||||
|
@JoinColumn(name = "category_id")
|
||||||
|
private Category category;
|
||||||
|
|
||||||
|
|
||||||
|
@ManyToMany
|
||||||
|
@JoinTable(name = "products_manufacturers",
|
||||||
|
joinColumns = @JoinColumn(name = "product_fk"),
|
||||||
|
inverseJoinColumns = @JoinColumn(name = "manufacturer_fk"))
|
||||||
|
private List<Manufacturer> manufacturerList;
|
||||||
|
|
||||||
|
@Column
|
||||||
|
private Integer price;
|
||||||
|
|
||||||
|
@Column
|
||||||
|
private String name;
|
||||||
|
|
||||||
|
@Lob
|
||||||
|
private byte[] photo;
|
||||||
|
|
||||||
|
public Product() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public Product(Integer price, byte[] photo, String name)
|
||||||
|
{
|
||||||
|
this.price = price;
|
||||||
|
this.photo = photo;
|
||||||
|
this.name = name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Product(ProductDTO productDTO){
|
||||||
|
this.price = productDTO.getPrice();
|
||||||
|
this.photo = productDTO.getPhoto().getBytes();
|
||||||
|
this.name = productDTO.getName();
|
||||||
|
}
|
||||||
|
|
||||||
|
public Long getId() { return id; }
|
||||||
|
|
||||||
|
public Integer getPrice() { return price; }
|
||||||
|
|
||||||
|
public byte[] getPhoto() { return photo; }
|
||||||
|
|
||||||
|
public String getName() { return name; }
|
||||||
|
|
||||||
|
public void setName(String name) { this.name = name; }
|
||||||
|
|
||||||
|
public void setPrice(Integer price) { this.price = price; }
|
||||||
|
|
||||||
|
public void setPhoto(byte[] photo) { this.photo = photo; }
|
||||||
|
|
||||||
|
public List<Manufacturer> getManufacturerList() { return manufacturerList; }
|
||||||
|
|
||||||
|
public Category getCategory() { return category; }
|
||||||
|
|
||||||
|
public void setCategory(Category category) { this.category = category; }
|
||||||
|
|
||||||
|
public void addManufacturer(Manufacturer manufacturer) {
|
||||||
|
manufacturerList.add(manufacturer);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void removeManufacturer(Manufacturer manufacturer) {
|
||||||
|
manufacturerList.remove(manufacturer);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object o) {
|
||||||
|
if (this == o) return true;
|
||||||
|
if (o == null || getClass() != o.getClass()) return false;
|
||||||
|
Product product = (Product) o;
|
||||||
|
return Objects.equals(id, product.id);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
return Objects.hash(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "Product{" +
|
||||||
|
"id=" + id +
|
||||||
|
", price='" + price + '\'' +
|
||||||
|
'}';
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,83 @@
|
|||||||
|
package ru.ulstu.is.sbapp.HardwareShop.models;
|
||||||
|
|
||||||
|
import jakarta.validation.constraints.NotBlank;
|
||||||
|
import jakarta.validation.constraints.Size;
|
||||||
|
|
||||||
|
import javax.persistence.*;
|
||||||
|
import java.util.Objects;
|
||||||
|
|
||||||
|
@Entity
|
||||||
|
@Table(name = "users")
|
||||||
|
public class User {
|
||||||
|
@Id
|
||||||
|
@GeneratedValue(strategy = GenerationType.AUTO)
|
||||||
|
private Long id;
|
||||||
|
@Column(nullable = false, unique = true, length = 64)
|
||||||
|
@NotBlank
|
||||||
|
@Size(min = 3, max = 64)
|
||||||
|
private String login;
|
||||||
|
@Column(nullable = false, length = 64)
|
||||||
|
@NotBlank
|
||||||
|
@Size(min = 6, max = 64)
|
||||||
|
private String password;
|
||||||
|
private UserRole role;
|
||||||
|
|
||||||
|
public User() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public User(String login, String password) {
|
||||||
|
this(login, password, UserRole.USER);
|
||||||
|
}
|
||||||
|
|
||||||
|
public User(String login, String password, UserRole role) {
|
||||||
|
this.login = login;
|
||||||
|
this.password = password;
|
||||||
|
this.role = role;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Long getId() {
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getLogin() {
|
||||||
|
return login;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setLogin(String login) {
|
||||||
|
this.login = login;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getPassword() {
|
||||||
|
return password;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setPassword(String password) {
|
||||||
|
this.password = password;
|
||||||
|
}
|
||||||
|
|
||||||
|
public UserRole getRole() {
|
||||||
|
return role;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object o) {
|
||||||
|
if (this == o) return true;
|
||||||
|
if (o == null || getClass() != o.getClass()) return false;
|
||||||
|
User user = (User) o;
|
||||||
|
return Objects.equals(id, user.id) && Objects.equals(login, user.login);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
return Objects.hash(id, login);
|
||||||
|
}
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "User{" +
|
||||||
|
"id=" + id +
|
||||||
|
", login='" + login + '\'' +
|
||||||
|
", password='" + password + '\'' +
|
||||||
|
", role='" + role + '\'' +
|
||||||
|
'}';
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,20 @@
|
|||||||
|
package ru.ulstu.is.sbapp.HardwareShop.models;
|
||||||
|
|
||||||
|
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();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static final class AsString {
|
||||||
|
public static final String ADMIN = PREFIX + "ADMIN";
|
||||||
|
public static final String USER = PREFIX + "USER";
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,11 @@
|
|||||||
|
package ru.ulstu.is.sbapp.HardwareShop.repository;
|
||||||
|
|
||||||
|
import org.springframework.data.jpa.repository.JpaRepository;
|
||||||
|
import org.springframework.data.jpa.repository.Query;
|
||||||
|
import ru.ulstu.is.sbapp.HardwareShop.models.Category;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public interface CategoryRepository extends JpaRepository<Category, Long> {
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,11 @@
|
|||||||
|
package ru.ulstu.is.sbapp.HardwareShop.repository;
|
||||||
|
|
||||||
|
import org.springframework.data.jpa.repository.JpaRepository;
|
||||||
|
import org.springframework.data.jpa.repository.Query;
|
||||||
|
import ru.ulstu.is.sbapp.HardwareShop.models.Manufacturer;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public interface ManufacturerRepository extends JpaRepository<Manufacturer, Long> {
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,13 @@
|
|||||||
|
package ru.ulstu.is.sbapp.HardwareShop.repository;
|
||||||
|
|
||||||
|
import org.springframework.data.jpa.repository.JpaRepository;
|
||||||
|
import org.springframework.data.jpa.repository.Query;
|
||||||
|
import ru.ulstu.is.sbapp.HardwareShop.models.Manufacturer;
|
||||||
|
import ru.ulstu.is.sbapp.HardwareShop.models.Product;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public interface ProductRepository extends JpaRepository<Product, Long> {
|
||||||
|
@Query("Select manufacturerList from Product where id =:id")
|
||||||
|
List<Manufacturer> getManufacturers(Long id);
|
||||||
|
}
|
@ -0,0 +1,8 @@
|
|||||||
|
package ru.ulstu.is.sbapp.HardwareShop.repository;
|
||||||
|
|
||||||
|
import org.springframework.data.jpa.repository.JpaRepository;
|
||||||
|
import ru.ulstu.is.sbapp.HardwareShop.models.User;
|
||||||
|
|
||||||
|
public interface UserRepository extends JpaRepository<User, Long> {
|
||||||
|
User findOneByLoginIgnoreCase(String login);
|
||||||
|
}
|
@ -0,0 +1,7 @@
|
|||||||
|
package ru.ulstu.is.sbapp.HardwareShop.services;
|
||||||
|
|
||||||
|
public class CategoryNotFoundException extends RuntimeException {
|
||||||
|
public CategoryNotFoundException(Long id) {
|
||||||
|
super(String.format("Category with id [%s] is not found", id));
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,92 @@
|
|||||||
|
package ru.ulstu.is.sbapp.HardwareShop.services;
|
||||||
|
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
import org.springframework.util.StringUtils;
|
||||||
|
import ru.ulstu.is.sbapp.HardwareShop.controller.CategoryDTO;
|
||||||
|
import ru.ulstu.is.sbapp.HardwareShop.controller.ProductDTO;
|
||||||
|
import ru.ulstu.is.sbapp.HardwareShop.models.Category;
|
||||||
|
|
||||||
|
import javax.persistence.EntityManager;
|
||||||
|
import javax.persistence.EntityNotFoundException;
|
||||||
|
import javax.persistence.PersistenceContext;
|
||||||
|
import org.springframework.transaction.annotation.Transactional;
|
||||||
|
import ru.ulstu.is.sbapp.HardwareShop.models.Product;
|
||||||
|
import ru.ulstu.is.sbapp.HardwareShop.repository.CategoryRepository;
|
||||||
|
import ru.ulstu.is.sbapp.Util.Validation.ValidatorUtil;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
|
@Service
|
||||||
|
public class CategoryService {
|
||||||
|
@Autowired
|
||||||
|
private final CategoryRepository categoryRepository;
|
||||||
|
@Autowired
|
||||||
|
private final ValidatorUtil validatorUtil;
|
||||||
|
|
||||||
|
public CategoryService(CategoryRepository categoryRepository, ValidatorUtil validatorUtil) {
|
||||||
|
this.categoryRepository = categoryRepository;
|
||||||
|
this.validatorUtil = validatorUtil;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Transactional
|
||||||
|
public Category addCategory(CategoryDTO categoryDTO) throws IOException {
|
||||||
|
final Category category = new Category(categoryDTO);
|
||||||
|
validatorUtil.validate(category);
|
||||||
|
return categoryRepository.save(category);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Transactional
|
||||||
|
public Category addCategory(String name) {
|
||||||
|
final Category category = new Category(name);
|
||||||
|
validatorUtil.validate(category);
|
||||||
|
return categoryRepository.save(category);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Transactional
|
||||||
|
public void saveCategory(Category category) {
|
||||||
|
categoryRepository.save(category);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Transactional(readOnly = true)
|
||||||
|
public Category findCategory(Long id) {
|
||||||
|
final Optional<Category> client = categoryRepository.findById(id);
|
||||||
|
return client.orElseThrow(() -> new CategoryNotFoundException(id));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Transactional(readOnly = true)
|
||||||
|
public List<Category> findAllCategories() {
|
||||||
|
return categoryRepository.findAll();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Transactional
|
||||||
|
public Category updateCategory(Long id, String name) {
|
||||||
|
final Category currentCategory = findCategory(id);
|
||||||
|
currentCategory.setName(name);
|
||||||
|
validatorUtil.validate(currentCategory);
|
||||||
|
return categoryRepository.save(currentCategory);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Transactional
|
||||||
|
public Category updateCategory(Long id, CategoryDTO categoryDTO) {
|
||||||
|
final Category currentCategory = findCategory(id);
|
||||||
|
currentCategory.setName(categoryDTO.getName());
|
||||||
|
validatorUtil.validate(currentCategory);
|
||||||
|
return categoryRepository.save(currentCategory);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Transactional
|
||||||
|
public Category deleteCategory(Long id) {
|
||||||
|
final Category currentCategory = findCategory(id);
|
||||||
|
categoryRepository.delete(currentCategory);
|
||||||
|
return currentCategory;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Transactional
|
||||||
|
public void deleteAllCategories() {
|
||||||
|
categoryRepository.deleteAll();
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,7 @@
|
|||||||
|
package ru.ulstu.is.sbapp.HardwareShop.services;
|
||||||
|
|
||||||
|
public class ManufacturerNotFoundException extends RuntimeException {
|
||||||
|
public ManufacturerNotFoundException(Long id) {
|
||||||
|
super(String.format("Manufacturer with id [%s] is not found", id));
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,97 @@
|
|||||||
|
package ru.ulstu.is.sbapp.HardwareShop.services;
|
||||||
|
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
import org.springframework.util.StringUtils;
|
||||||
|
import ru.ulstu.is.sbapp.HardwareShop.controller.ManufacturerDTO;
|
||||||
|
import ru.ulstu.is.sbapp.HardwareShop.controller.ProductDTO;
|
||||||
|
import ru.ulstu.is.sbapp.HardwareShop.models.Category;
|
||||||
|
import ru.ulstu.is.sbapp.HardwareShop.models.Manufacturer;
|
||||||
|
|
||||||
|
|
||||||
|
import javax.persistence.EntityManager;
|
||||||
|
import javax.persistence.EntityNotFoundException;
|
||||||
|
import javax.persistence.PersistenceContext;
|
||||||
|
import org.springframework.transaction.annotation.Transactional;
|
||||||
|
import ru.ulstu.is.sbapp.HardwareShop.models.Product;
|
||||||
|
import ru.ulstu.is.sbapp.HardwareShop.repository.ManufacturerRepository;
|
||||||
|
import ru.ulstu.is.sbapp.Util.Validation.ValidatorUtil;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
|
@Service
|
||||||
|
public class ManufacturerService {
|
||||||
|
@Autowired
|
||||||
|
private final ManufacturerRepository manufacturerRepository;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private final ValidatorUtil validatorUtil;
|
||||||
|
|
||||||
|
public ManufacturerService(ManufacturerRepository manufacturerRepository, ValidatorUtil validatorUtil) {
|
||||||
|
this.manufacturerRepository = manufacturerRepository;
|
||||||
|
this.validatorUtil = validatorUtil;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Transactional
|
||||||
|
public Manufacturer addManufacturer(String name, String address) {
|
||||||
|
final Manufacturer manufacturer = new Manufacturer(name, address);
|
||||||
|
validatorUtil.validate(manufacturer);
|
||||||
|
return manufacturerRepository.save(manufacturer);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Transactional
|
||||||
|
public Manufacturer addManufacturer(ManufacturerDTO manufacturerDTO) throws IOException {
|
||||||
|
final Manufacturer manufacturer = new Manufacturer(manufacturerDTO);
|
||||||
|
validatorUtil.validate(manufacturer);
|
||||||
|
return manufacturerRepository.save(manufacturer);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Transactional
|
||||||
|
public void saveManufacturer(Manufacturer manufacturer) {
|
||||||
|
manufacturerRepository.save(manufacturer);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Transactional(readOnly = true)
|
||||||
|
public Manufacturer findManufacturer(Long id) {
|
||||||
|
final Optional<Manufacturer> client = manufacturerRepository.findById(id);
|
||||||
|
return client.orElseThrow(() -> new ManufacturerNotFoundException(id));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Transactional(readOnly = true)
|
||||||
|
public List<Manufacturer> findAllManufacturers() {
|
||||||
|
return manufacturerRepository.findAll();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Transactional
|
||||||
|
public Manufacturer updateManufacturer(Long id, String name, String address) {
|
||||||
|
final Manufacturer currentManufacturer = findManufacturer(id);
|
||||||
|
currentManufacturer.setName(name);
|
||||||
|
currentManufacturer.setAddress(address);
|
||||||
|
validatorUtil.validate(currentManufacturer);
|
||||||
|
return manufacturerRepository.save(currentManufacturer);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Transactional
|
||||||
|
public Manufacturer updateManufacturer(Long id, ManufacturerDTO manufacturerDTO) {
|
||||||
|
final Manufacturer currentManufacturer = findManufacturer(id);
|
||||||
|
currentManufacturer.setName(manufacturerDTO.getName());
|
||||||
|
currentManufacturer.setAddress(manufacturerDTO.getAddress());
|
||||||
|
validatorUtil.validate(currentManufacturer);
|
||||||
|
return manufacturerRepository.save(currentManufacturer);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Transactional
|
||||||
|
public Manufacturer deleteManufacturer(Long id) {
|
||||||
|
final Manufacturer currentManufacturer = findManufacturer(id);
|
||||||
|
manufacturerRepository.delete(currentManufacturer);
|
||||||
|
return currentManufacturer;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Transactional
|
||||||
|
public void deleteAllManufacturers() {
|
||||||
|
manufacturerRepository.deleteAll();
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,7 @@
|
|||||||
|
package ru.ulstu.is.sbapp.HardwareShop.services;
|
||||||
|
|
||||||
|
public class ProductNotFoundException extends RuntimeException {
|
||||||
|
public ProductNotFoundException(Long id) {
|
||||||
|
super(String.format("Product with id [%s] is not found", id));
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,157 @@
|
|||||||
|
package ru.ulstu.is.sbapp.HardwareShop.services;
|
||||||
|
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
import org.springframework.util.StringUtils;
|
||||||
|
import org.springframework.web.multipart.MultipartFile;
|
||||||
|
import ru.ulstu.is.sbapp.HardwareShop.controller.ManufacturerDTO;
|
||||||
|
import ru.ulstu.is.sbapp.HardwareShop.controller.ProductDTO;
|
||||||
|
import ru.ulstu.is.sbapp.HardwareShop.models.Manufacturer;
|
||||||
|
import ru.ulstu.is.sbapp.HardwareShop.models.Product;
|
||||||
|
|
||||||
|
import javax.persistence.EntityManager;
|
||||||
|
import javax.persistence.EntityNotFoundException;
|
||||||
|
import javax.persistence.PersistenceContext;
|
||||||
|
import org.springframework.transaction.annotation.Transactional;
|
||||||
|
import ru.ulstu.is.sbapp.HardwareShop.repository.ManufacturerRepository;
|
||||||
|
import ru.ulstu.is.sbapp.HardwareShop.repository.ProductRepository;
|
||||||
|
import ru.ulstu.is.sbapp.Util.Validation.ValidatorUtil;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
|
@Service
|
||||||
|
public class ProductService {
|
||||||
|
@Autowired
|
||||||
|
private final ProductRepository productRepository;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private final ManufacturerService manufacturerService;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private final CategoryService categoryService;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private final ValidatorUtil validatorUtil;
|
||||||
|
|
||||||
|
public ProductService(ProductRepository productRepository, ValidatorUtil validatorUtil, ManufacturerService manufacturerService, CategoryService categoryService) {
|
||||||
|
this.productRepository = productRepository;
|
||||||
|
this.validatorUtil = validatorUtil;
|
||||||
|
this.manufacturerService = manufacturerService;
|
||||||
|
this.categoryService = categoryService;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Transactional
|
||||||
|
public Product addProduct(ProductDTO productDTO) throws IOException {
|
||||||
|
final Product product = new Product(productDTO);
|
||||||
|
product.setCategory(categoryService.findCategory(productDTO.getCategory_id()));
|
||||||
|
validatorUtil.validate(product);
|
||||||
|
return productRepository.save(product);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Transactional
|
||||||
|
public Product addProduct(Integer price, byte[] image, String name) {
|
||||||
|
final Product product = new Product(price, image, name);
|
||||||
|
validatorUtil.validate(product);
|
||||||
|
return productRepository.save(product);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Transactional(readOnly = true)
|
||||||
|
public Product findProduct(Long id) {
|
||||||
|
final Optional<Product> post = productRepository.findById(id);
|
||||||
|
return post.orElseThrow(() -> new ProductNotFoundException(id));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Transactional
|
||||||
|
public void saveProduct(Product product) {
|
||||||
|
productRepository.save(product);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Transactional(readOnly = true)
|
||||||
|
public List<Product> findAllProducts() {
|
||||||
|
return productRepository.findAll();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Transactional
|
||||||
|
public Product updateProduct(Long id, ProductDTO productDTO) {
|
||||||
|
final Product currentProduct = findProduct(id);
|
||||||
|
currentProduct.setName(productDTO.getName());
|
||||||
|
currentProduct.setPrice(productDTO.getPrice());
|
||||||
|
currentProduct.setPhoto(productDTO.getPhoto().getBytes(StandardCharsets.UTF_8));
|
||||||
|
currentProduct.setCategory(categoryService.findCategory(productDTO.getCategory_id()));
|
||||||
|
validatorUtil.validate(currentProduct);
|
||||||
|
return productRepository.save(currentProduct);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Transactional
|
||||||
|
public Product updateProduct(Long id, Integer price, byte[] image) {
|
||||||
|
final Product currentProduct = findProduct(id);
|
||||||
|
currentProduct.setPrice(price);
|
||||||
|
/*currentProduct.setPhoto(image);*/
|
||||||
|
validatorUtil.validate(currentProduct);
|
||||||
|
return productRepository.save(currentProduct);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Transactional
|
||||||
|
public Product deleteProduct(Long id) {
|
||||||
|
final Product currentProduct = findProduct(id);
|
||||||
|
productRepository.delete(currentProduct);
|
||||||
|
return currentProduct;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Transactional
|
||||||
|
public void deleteAllProducts() {
|
||||||
|
productRepository.deleteAll();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Transactional
|
||||||
|
public void addManufacturersToProduct(Long id, Long manufacturer_id) {
|
||||||
|
final Product currentProduct = findProduct(id);
|
||||||
|
final Manufacturer manufacturer = manufacturerService.findManufacturer(manufacturer_id);
|
||||||
|
currentProduct.addManufacturer(manufacturer);
|
||||||
|
validatorUtil.validate(currentProduct);
|
||||||
|
productRepository.save(currentProduct);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Transactional
|
||||||
|
public void removeManufacturersToProduct(Long id, Long manufacturer_id) {
|
||||||
|
final Product currentProduct = findProduct(id);
|
||||||
|
currentProduct.removeManufacturer(manufacturerService.findManufacturer(manufacturer_id));
|
||||||
|
validatorUtil.validate(currentProduct);
|
||||||
|
productRepository.save(currentProduct);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Transactional
|
||||||
|
public List<Manufacturer> getProductManufacturers(Long id){
|
||||||
|
return productRepository.findById(id).get().getManufacturerList();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Transactional
|
||||||
|
public List<Product> getProductsFilter(List<ManufacturerDTO> manufacturerDTOS) {
|
||||||
|
List<Manufacturer> manufacturers = new ArrayList<>();
|
||||||
|
for(ManufacturerDTO mnfctrDTO : manufacturerDTOS) {
|
||||||
|
manufacturers.add(manufacturerService.findManufacturer(mnfctrDTO.getId()));
|
||||||
|
}
|
||||||
|
|
||||||
|
List<Product> filter = findAllProducts();
|
||||||
|
|
||||||
|
for(Product product : filter) {
|
||||||
|
boolean flag = false;
|
||||||
|
for (Manufacturer manufacturer : product.getManufacturerList()) {
|
||||||
|
if (manufacturers.contains(manufacturer)) {
|
||||||
|
flag = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!flag)
|
||||||
|
{
|
||||||
|
filter.remove(product);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return filter;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,7 @@
|
|||||||
|
package ru.ulstu.is.sbapp.HardwareShop.services;
|
||||||
|
|
||||||
|
public class UserNotFoundException extends RuntimeException{
|
||||||
|
public UserNotFoundException(Long id) {
|
||||||
|
super(String.format("User with id [%s] is not found", id));
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,66 @@
|
|||||||
|
package ru.ulstu.is.sbapp.HardwareShop.services;
|
||||||
|
|
||||||
|
import org.springframework.data.domain.Page;
|
||||||
|
import org.springframework.data.domain.PageRequest;
|
||||||
|
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.stereotype.Service;
|
||||||
|
import org.springframework.transaction.annotation.Transactional;
|
||||||
|
import ru.ulstu.is.sbapp.HardwareShop.models.User;
|
||||||
|
import ru.ulstu.is.sbapp.HardwareShop.models.UserRole;
|
||||||
|
import ru.ulstu.is.sbapp.HardwareShop.repository.UserRepository;
|
||||||
|
|
||||||
|
import jakarta.validation.ValidationException;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.Objects;
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
|
@Service
|
||||||
|
public class UserService implements UserDetailsService {
|
||||||
|
private final UserRepository userRepository;
|
||||||
|
private final PasswordEncoder passwordEncoder;
|
||||||
|
|
||||||
|
public UserService(UserRepository userRepository,
|
||||||
|
PasswordEncoder passwordEncoder) {
|
||||||
|
this.userRepository = userRepository;
|
||||||
|
this.passwordEncoder = passwordEncoder;
|
||||||
|
}
|
||||||
|
public Page<User> findAllPages(int page, int size) {
|
||||||
|
return userRepository.findAll(PageRequest.of(page - 1, size, Sort.by("id").ascending()));
|
||||||
|
}
|
||||||
|
public User findByLogin(String login) {
|
||||||
|
return userRepository.findOneByLoginIgnoreCase(login);
|
||||||
|
}
|
||||||
|
public User createUser(String login, String password, String passwordConfirm) {
|
||||||
|
return createUser(login, password, passwordConfirm, UserRole.USER);
|
||||||
|
}
|
||||||
|
public User createUser(String login, String password, String passwordConfirm, UserRole role) {
|
||||||
|
if (findByLogin(login) != null) {
|
||||||
|
throw new ValidationException(String.format("User '%s' already exists", login));
|
||||||
|
}
|
||||||
|
final User user = new User(login, passwordEncoder.encode(password), role);
|
||||||
|
if (!Objects.equals(password, passwordConfirm)) {
|
||||||
|
throw new ValidationException("Passwords not equals");
|
||||||
|
}
|
||||||
|
return userRepository.save(user);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
|
||||||
|
final User userEntity = findByLogin(username);
|
||||||
|
if (userEntity == null) {
|
||||||
|
throw new UsernameNotFoundException(username);
|
||||||
|
}
|
||||||
|
return new org.springframework.security.core.userdetails.User(
|
||||||
|
userEntity.getLogin(), userEntity.getPassword(), Collections.singleton(userEntity.getRole()));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Transactional(readOnly = true)
|
||||||
|
public User findUser(Long id) {
|
||||||
|
final Optional<User> user = userRepository.findById(id);
|
||||||
|
return user.orElseThrow(() -> new UserNotFoundException(id));
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,14 @@
|
|||||||
|
package ru.ulstu.is.sbapp;
|
||||||
|
|
||||||
|
import org.springframework.context.annotation.Bean;
|
||||||
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
|
||||||
|
import org.springframework.security.crypto.password.PasswordEncoder;
|
||||||
|
|
||||||
|
@Configuration
|
||||||
|
public class PasswordEncoderConfiguration {
|
||||||
|
@Bean
|
||||||
|
public PasswordEncoder createPasswordEncoder() {
|
||||||
|
return new BCryptPasswordEncoder();
|
||||||
|
}
|
||||||
|
}
|
@ -13,38 +13,4 @@ public class SbappApplication {
|
|||||||
public static void main(String[] args) {
|
public static void main(String[] args) {
|
||||||
SpringApplication.run(SbappApplication.class, args);
|
SpringApplication.run(SbappApplication.class, args);
|
||||||
}
|
}
|
||||||
|
|
||||||
@GetMapping("/hello")
|
|
||||||
public String hello() {
|
|
||||||
return "Hello, i'm working!";
|
|
||||||
}
|
|
||||||
|
|
||||||
@GetMapping("/ToUpper")
|
|
||||||
public String ToUpper(@RequestParam(value = "", defaultValue = "default") String text) {
|
|
||||||
return String.format(text).toUpperCase();
|
|
||||||
}
|
|
||||||
|
|
||||||
@GetMapping("/calc")
|
|
||||||
public Integer calc(@RequestParam(defaultValue = "10") Integer num1,
|
|
||||||
@RequestParam(defaultValue = "10") Integer num2) {
|
|
||||||
return num1 + num2;
|
|
||||||
}
|
|
||||||
|
|
||||||
@GetMapping("/concate")
|
|
||||||
public String concate(@RequestParam String text) {
|
|
||||||
return "Word-"+ text;
|
|
||||||
}
|
|
||||||
|
|
||||||
@GetMapping("/divider")
|
|
||||||
public String IsInt(@RequestParam int num1,
|
|
||||||
@RequestParam(defaultValue = "1") int num2) {
|
|
||||||
if (num1 % num2 == 0)
|
|
||||||
{
|
|
||||||
return String.format("Делится на цело, результат: %s", num1 / num2);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
return String.format("Неделится на цело, результат: %s", num1 / num2 + " Остаток: " + num1 % num2);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
67
src/main/java/ru/ulstu/is/sbapp/SecurityConfiguration.java
Normal file
67
src/main/java/ru/ulstu/is/sbapp/SecurityConfiguration.java
Normal file
@ -0,0 +1,67 @@
|
|||||||
|
package ru.ulstu.is.sbapp;
|
||||||
|
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
import org.springframework.context.annotation.Bean;
|
||||||
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
import org.springframework.http.HttpMethod;
|
||||||
|
import org.springframework.security.config.annotation.method.configuration.EnableMethodSecurity;
|
||||||
|
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.configuration.WebSecurityCustomizer;
|
||||||
|
import org.springframework.security.web.SecurityFilterChain;
|
||||||
|
import ru.ulstu.is.sbapp.HardwareShop.controller.UserSignUpMvcController;
|
||||||
|
import ru.ulstu.is.sbapp.HardwareShop.models.UserRole;
|
||||||
|
import ru.ulstu.is.sbapp.HardwareShop.services.UserService;
|
||||||
|
|
||||||
|
@Configuration
|
||||||
|
@EnableWebSecurity
|
||||||
|
@EnableMethodSecurity(
|
||||||
|
securedEnabled = true
|
||||||
|
)
|
||||||
|
public class SecurityConfiguration {
|
||||||
|
private final Logger log = LoggerFactory.getLogger(SecurityConfiguration.class);
|
||||||
|
private static final String LOGIN_URL = "/login";
|
||||||
|
private final UserService userService;
|
||||||
|
|
||||||
|
public SecurityConfiguration(UserService userService) {
|
||||||
|
this.userService = userService;
|
||||||
|
createAdminOnStartup();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void createAdminOnStartup() {
|
||||||
|
final String admin = "admin";
|
||||||
|
if (userService.findByLogin(admin) == null) {
|
||||||
|
log.info("Admin user successfully created");
|
||||||
|
userService.createUser(admin, admin, admin, UserRole.ADMIN);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
|
||||||
|
http.headers().frameOptions().sameOrigin().and()
|
||||||
|
.cors().and()
|
||||||
|
.csrf().disable()
|
||||||
|
.authorizeHttpRequests()
|
||||||
|
.antMatchers(UserSignUpMvcController.SIGNUP_URL).permitAll()
|
||||||
|
.antMatchers(HttpMethod.GET, LOGIN_URL).permitAll()
|
||||||
|
.anyRequest().authenticated()
|
||||||
|
.and()
|
||||||
|
.formLogin()
|
||||||
|
.loginPage(LOGIN_URL).permitAll()
|
||||||
|
.defaultSuccessUrl("/product", true)
|
||||||
|
.and()
|
||||||
|
.logout().permitAll();
|
||||||
|
return http.userDetailsService(userService).build();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
public WebSecurityCustomizer webSecurityCustomizer() {
|
||||||
|
return (web) -> web.ignoring()
|
||||||
|
.antMatchers("/css/**")
|
||||||
|
.antMatchers("/js/**")
|
||||||
|
.antMatchers("/templates/**")
|
||||||
|
.antMatchers("/webjars/**");
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,37 @@
|
|||||||
|
package ru.ulstu.is.sbapp.Util.Errors;
|
||||||
|
|
||||||
|
import org.springframework.web.bind.annotation.ControllerAdvice;
|
||||||
|
import org.springframework.web.bind.annotation.ExceptionHandler;
|
||||||
|
import org.springframework.context.support.DefaultMessageSourceResolvable;
|
||||||
|
import org.springframework.http.HttpStatus;
|
||||||
|
import org.springframework.http.ResponseEntity;
|
||||||
|
import org.springframework.web.bind.MethodArgumentNotValidException;
|
||||||
|
|
||||||
|
import org.springframework.web.bind.annotation.RestController;
|
||||||
|
import ru.ulstu.is.sbapp.Util.Validation.ValidationException;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
@ControllerAdvice(annotations = RestController.class)
|
||||||
|
public class AdviceController {
|
||||||
|
@ExceptionHandler({
|
||||||
|
ValidationException.class
|
||||||
|
})
|
||||||
|
public ResponseEntity<Object> handleException(Throwable e) {
|
||||||
|
return new ResponseEntity<>(e.getMessage(), HttpStatus.BAD_REQUEST);
|
||||||
|
}
|
||||||
|
|
||||||
|
@ExceptionHandler(MethodArgumentNotValidException.class)
|
||||||
|
public ResponseEntity<Object> handleBindException(MethodArgumentNotValidException e) {
|
||||||
|
final ValidationException validationException = new ValidationException(
|
||||||
|
e.getBindingResult().getAllErrors().stream()
|
||||||
|
.map(DefaultMessageSourceResolvable::getDefaultMessage)
|
||||||
|
.collect(Collectors.toSet()));
|
||||||
|
return handleException(validationException);
|
||||||
|
}
|
||||||
|
|
||||||
|
@ExceptionHandler(Exception.class)
|
||||||
|
public ResponseEntity<Object> handleUnknownException(Throwable e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
return new ResponseEntity<>(e.getMessage(), HttpStatus.INTERNAL_SERVER_ERROR);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,9 @@
|
|||||||
|
package ru.ulstu.is.sbapp.Util.Validation;
|
||||||
|
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
public class ValidationException extends RuntimeException {
|
||||||
|
public ValidationException(Set<String> errors) {
|
||||||
|
super(String.join("\n", errors));
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,30 @@
|
|||||||
|
package ru.ulstu.is.sbapp.Util.Validation;
|
||||||
|
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
import javax.validation.ConstraintViolation;
|
||||||
|
import javax.validation.Validation;
|
||||||
|
import javax.validation.Validator;
|
||||||
|
import javax.validation.ValidatorFactory;
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
@Component
|
||||||
|
public class ValidatorUtil {
|
||||||
|
private final Validator validator;
|
||||||
|
|
||||||
|
public ValidatorUtil() {
|
||||||
|
try (ValidatorFactory factory = Validation.buildDefaultValidatorFactory()) {
|
||||||
|
this.validator = factory.getValidator();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public <T> void validate(T object) {
|
||||||
|
final Set<ConstraintViolation<T>> errors = validator.validate(object);
|
||||||
|
if (!errors.isEmpty()) {
|
||||||
|
throw new ValidationException(errors.stream()
|
||||||
|
.map(ConstraintViolation::getMessage)
|
||||||
|
.collect(Collectors.toSet()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
22
src/main/java/ru/ulstu/is/sbapp/WebConfiguration.java
Normal file
22
src/main/java/ru/ulstu/is/sbapp/WebConfiguration.java
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
package ru.ulstu.is.sbapp;
|
||||||
|
|
||||||
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
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 {
|
||||||
|
public static final String REST_API = "/api";
|
||||||
|
@Override
|
||||||
|
public void addViewControllers(ViewControllerRegistry registry) {
|
||||||
|
WebMvcConfigurer.super.addViewControllers(registry);
|
||||||
|
registry.addViewController("login");
|
||||||
|
registry.addViewController("product");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void addCorsMappings(CorsRegistry registry) {
|
||||||
|
registry.addMapping("/**").allowedMethods("*");
|
||||||
|
}
|
||||||
|
}
|
@ -1 +1,12 @@
|
|||||||
|
spring.main.banner-mode=off
|
||||||
|
#server.port=8080
|
||||||
|
spring.datasource.url=jdbc:h2:file:./data
|
||||||
|
spring.datasource.driverClassName=org.h2.Driver
|
||||||
|
spring.datasource.username=sa
|
||||||
|
spring.datasource.password=password
|
||||||
|
spring.jpa.database-platform=org.hibernate.dialect.H2Dialect
|
||||||
|
spring.jpa.hibernate.ddl-auto=update
|
||||||
|
spring.h2.console.enabled=true
|
||||||
|
spring.h2.console.settings.trace=false
|
||||||
|
spring.h2.console.settings.web-allow-others=false
|
||||||
|
jwt.dev-token=my-secret-jwt
|
@ -1,3 +0,0 @@
|
|||||||
h1 {
|
|
||||||
color: red;
|
|
||||||
}
|
|
@ -1,7 +0,0 @@
|
|||||||
function test() {
|
|
||||||
fetch("http://localhost:8080/hello", {
|
|
||||||
mode:
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
test();
|
|
49
src/main/resources/templates/categories.html
Normal file
49
src/main/resources/templates/categories.html
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en"
|
||||||
|
xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout"
|
||||||
|
layout:decorate="~{default}">
|
||||||
|
<head>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div layout:fragment="content">
|
||||||
|
<div>
|
||||||
|
<a class="btn btn-success button-fixed"
|
||||||
|
th:href="@{/category/update/}">
|
||||||
|
<i class="fa-solid fa-plus"></i> Добавить
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
<div class="table-responsive">
|
||||||
|
<table class="table">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th scope="col">Название</th>
|
||||||
|
<th scope="col">Редактировать запись</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<tr th:each="category, iterator: ${categories}">
|
||||||
|
<td th:text="${category.name}" style="width: 60%"/>
|
||||||
|
<td style="width: 10%">
|
||||||
|
<div class="btn-group" role="group" aria-label="Basic example">
|
||||||
|
<a class="btn btn-warning button-fixed button-sm"
|
||||||
|
th:href="@{/category/update/{id}(id=${category.id})}">
|
||||||
|
<i class="fa fa-pencil" aria-hidden="true"></i> Изменить
|
||||||
|
</a>
|
||||||
|
<button type="button" class="btn btn-danger button-fixed button-sm"
|
||||||
|
th:attr="onclick=|confirm('Удалить запись?') && document.getElementById('remove-${category.id}').click()|">
|
||||||
|
<i class="fa fa-trash" aria-hidden="true"></i> Удалить
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<form th:action="@{/category/delete/{id}(id=${category.id})}" method="post">
|
||||||
|
<button th:id="'remove-' + ${category.id}" type="submit" style="display: none">
|
||||||
|
Удалить
|
||||||
|
</button>
|
||||||
|
</form>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
31
src/main/resources/templates/category-update.html
Normal file
31
src/main/resources/templates/category-update.html
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en"
|
||||||
|
xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout"
|
||||||
|
layout:decorate="~{default}">
|
||||||
|
<head>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div layout:fragment="content">
|
||||||
|
<div th:text="${errors}" class="margin-bottom alert-danger"></div>
|
||||||
|
<form action="#" th:action="@{/category/{id}(id=${id})}" th:object="${categoryDto}" method="post">
|
||||||
|
<div class="mb-3">
|
||||||
|
<label for="name" class="form-label">Название</label>
|
||||||
|
<input type="text" class="form-control" id="name" th:field="${categoryDto.name}" required="true">
|
||||||
|
</div>
|
||||||
|
<div class="mb-3">
|
||||||
|
<button type="submit" class="btn btn-success button-fixed">
|
||||||
|
<span th:if="${id == null}">
|
||||||
|
<i class="fa-solid fa-plus"></i> Добавить
|
||||||
|
</span>
|
||||||
|
<span th:if="${id != null}">
|
||||||
|
<i class="fa fa-pencil" aria-hidden="true"></i> Сохранить
|
||||||
|
</span>
|
||||||
|
</button>
|
||||||
|
<a class="btn btn-secondary button-fixed" th:href="@{/category}">
|
||||||
|
Назад
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
52
src/main/resources/templates/default.html
Normal file
52
src/main/resources/templates/default.html
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en"
|
||||||
|
xmlns:th="http://www.thymeleaf.org"
|
||||||
|
xmlns:sec="http://www.thymeleaf.org/thymeleaf-extras-springsecurity5"
|
||||||
|
xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout">
|
||||||
|
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<title>Онлайн магазин</title>
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1"/>
|
||||||
|
<link rel="icon" href="/favicon.svg">
|
||||||
|
<script type="text/javascript" src="/webjars/bootstrap/5.1.3/js/bootstrap.bundle.min.js"></script>
|
||||||
|
<link rel="stylesheet" href="/webjars/bootstrap/5.1.3/css/bootstrap.min.css"/>
|
||||||
|
<link rel="stylesheet" href="/webjars/font-awesome/6.1.0/css/all.min.css"/>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<header class="fixed-top">
|
||||||
|
<nav class="navbar navbar-expand-lg bg-success" data-bs-theme="dark">
|
||||||
|
<div class="container">
|
||||||
|
<a class="navbar-brand" href="#">
|
||||||
|
<strong class="text-white"> Online Shop </strong>
|
||||||
|
</a>
|
||||||
|
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarNavAltMarkup" aria-controls="navbarNavAltMarkup" aria-expanded="false" aria-label="Toggle navigation">
|
||||||
|
<span class="navbar-toggler-icon"></span>
|
||||||
|
</button>
|
||||||
|
<div class="collapse navbar-collapse" id="navbarNavAltMarkup">
|
||||||
|
<div class="navbar-nav" th:with="activeLink=${#request.requestURI}">
|
||||||
|
<a class="nav-link active text-white" aria-current="page" href="/product" th:classappend="${#strings.equals(activeLink, '/product')} ? 'active' : ''">Products</a>
|
||||||
|
<a class="nav-link active text-white" href="/category" th:classappend="${#strings.equals(activeLink, '/category')} ? 'active' : ''">Categories</a>
|
||||||
|
<a class="nav-link active text-white" href="/manufacturer" th:classappend="${#strings.equals(activeLink, '/manufacturer')} ? 'active' : ''">Manufacturers</a>
|
||||||
|
<a sec:authorize="hasRole('ROLE_ADMIN')" class="nav-link" href="/users" th:classappend="${#strings.equals(activeLink, '/users')} ? 'active' : ''">Пользователи</a>
|
||||||
|
<a class="nav-link active text-white" href="/logout" th:classappend="${#strings.equals(activeLink, '/login')} ? 'active' : ''">Logout</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</nav>
|
||||||
|
</header>
|
||||||
|
<div class="container-fluid" style="margin-top: 50pt;">
|
||||||
|
<div class="container container-padding" layout:fragment="content">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<footer class="container pt-4 my-md-5 pt-md-5 text-center border-top">
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-12 col-md">
|
||||||
|
<h5 class=""><strong>End</strong></h5>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</footer>
|
||||||
|
</body>
|
||||||
|
<th:block layout:fragment="scripts">
|
||||||
|
</th:block>
|
||||||
|
</html>
|
10
src/main/resources/templates/error.html
Normal file
10
src/main/resources/templates/error.html
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<title>Title</title>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
|
||||||
|
</body>
|
||||||
|
</html>
|
@ -1,11 +0,0 @@
|
|||||||
<!DOCTYPE html>
|
|
||||||
<html xmlns:th = "http://www.thymeleaf.org">
|
|
||||||
<head>
|
|
||||||
<link rel="stylesheet" href="/styles.css">
|
|
||||||
</head>
|
|
||||||
|
|
||||||
<body>
|
|
||||||
<h1> IA WORK!</h1>
|
|
||||||
</body>
|
|
||||||
|
|
||||||
</html>
|
|
42
src/main/resources/templates/login.html
Normal file
42
src/main/resources/templates/login.html
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en"
|
||||||
|
xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout"
|
||||||
|
xmlns:th="http://www.w3.org/1999/xhtml"
|
||||||
|
layout:decorate="~{default}">
|
||||||
|
<body>
|
||||||
|
<div layout:fragment="content">
|
||||||
|
<div th:if="${param.error}" class="alert alert-danger margin-bottom">
|
||||||
|
User not found
|
||||||
|
</div>
|
||||||
|
<div th:if="${param.logout}" class="alert alert-success margin-bottom">
|
||||||
|
Logout success
|
||||||
|
</div>
|
||||||
|
<div th:if="${param.created}" class="alert alert-success margin-bottom">
|
||||||
|
User '<span th:text="${param.created}"></span>' was successfully created
|
||||||
|
</div>
|
||||||
|
<form th:action="@{/login}" method="post">
|
||||||
|
<div class="mb-3">
|
||||||
|
<p class="mb-1">Login</p>
|
||||||
|
<input name="username" id="username" class="form-control"
|
||||||
|
type="text" required autofocus />
|
||||||
|
</div>
|
||||||
|
<div class="mb-3">
|
||||||
|
<p class="mb-1">Password</p>
|
||||||
|
<input name="password" id="password" class="form-control"
|
||||||
|
type="password" required />
|
||||||
|
</div>
|
||||||
|
<div class="mb-3">
|
||||||
|
<button type="submit" class="btn btn-success">
|
||||||
|
Sign in
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<p>
|
||||||
|
<span>Not a member yet?</span>
|
||||||
|
<a href="/signup">Sign Up here</a>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
33
src/main/resources/templates/manufacturer-update.html
Normal file
33
src/main/resources/templates/manufacturer-update.html
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en"
|
||||||
|
xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout"
|
||||||
|
layout:decorate="~{default}">
|
||||||
|
<head>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div layout:fragment="content">
|
||||||
|
<div th:text="${errors}" class="margin-bottom alert-danger"></div>
|
||||||
|
<form action="#" th:action="@{/manufacturer/{id}(id=${id})}" th:object="${manufacturerDto}" method="post">
|
||||||
|
<div class="mb-3">
|
||||||
|
<label for="name" class="form-label">Название</label>
|
||||||
|
<input type="text" class="form-control" id="name" th:field="${manufacturerDto.name}" required="true">
|
||||||
|
<label for="name" class="form-label">Адресс</label>
|
||||||
|
<input type="text" class="form-control" id="address" th:field="${manufacturerDto.address}" required="true">
|
||||||
|
</div>
|
||||||
|
<div class="mb-3">
|
||||||
|
<button type="submit" class="btn btn-success button-fixed">
|
||||||
|
<span th:if="${id == null}">
|
||||||
|
<i class="fa-solid fa-plus"></i> Добавить
|
||||||
|
</span>
|
||||||
|
<span th:if="${id != null}">
|
||||||
|
<i class="fa fa-pencil" aria-hidden="true"></i> Сохранить
|
||||||
|
</span>
|
||||||
|
</button>
|
||||||
|
<a class="btn btn-secondary button-fixed" th:href="@{/manufacturer}">
|
||||||
|
Назад
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
51
src/main/resources/templates/manufacturers.html
Normal file
51
src/main/resources/templates/manufacturers.html
Normal file
@ -0,0 +1,51 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en"
|
||||||
|
xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout"
|
||||||
|
layout:decorate="~{default}">
|
||||||
|
<head>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div layout:fragment="content">
|
||||||
|
<div>
|
||||||
|
<a class="btn btn-success button-fixed"
|
||||||
|
th:href="@{/manufacturer/update/}">
|
||||||
|
<i class="fa-solid fa-plus"></i> Добавить
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
<div class="table-responsive">
|
||||||
|
<table class="table">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th scope="col">Название</th>
|
||||||
|
<th scope="col">Адресс</th>
|
||||||
|
<th scope="col">Редактировать запись</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<tr th:each="manufacturer, iterator: ${manufacturers}">
|
||||||
|
<td th:text="${manufacturer.name}" style="width: 35%"/>
|
||||||
|
<td th:text="${manufacturer.address}" style="width: 35%"/>
|
||||||
|
<td style="width: 10%">
|
||||||
|
<div class="btn-group" role="group" aria-label="Basic example">
|
||||||
|
<a class="btn btn-warning button-fixed button-sm"
|
||||||
|
th:href="@{/manufacturer/update/{id}(id=${manufacturer.id})}">
|
||||||
|
<i class="fa fa-pencil" aria-hidden="true"></i> Изменить
|
||||||
|
</a>
|
||||||
|
<button type="button" class="btn btn-danger button-fixed button-sm"
|
||||||
|
th:attr="onclick=|confirm('Удалить запись?') && document.getElementById('remove-${manufacturer.id}').click()|">
|
||||||
|
<i class="fa fa-trash" aria-hidden="true"></i> Удалить
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<form th:action="@{/manufacturer/delete/{id}(id=${manufacturer.id})}" method="post">
|
||||||
|
<button th:id="'remove-' + ${manufacturer.id}" type="submit" style="display: none">
|
||||||
|
Удалить
|
||||||
|
</button>
|
||||||
|
</form>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
49
src/main/resources/templates/product-filter.html
Normal file
49
src/main/resources/templates/product-filter.html
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en"
|
||||||
|
xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout"
|
||||||
|
layout:decorate="~{default}">
|
||||||
|
<head>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div layout:fragment="content">
|
||||||
|
<div class="table-responsive">
|
||||||
|
<table class="table">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th scope="col">Название</th>
|
||||||
|
<th scope="col">Адресс</th>
|
||||||
|
<th scope="col">Редактировать запись</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<tr th:each="manufacturer, iterator: ${selected}">
|
||||||
|
<td th:text="${manufacturer.name}" style="width: 35%"/>
|
||||||
|
<td th:text="${manufacturer.address}" style="width: 35%"/>
|
||||||
|
<td style="width: 10%">
|
||||||
|
<div class="btn-group" role="group" aria-label="Basic example">
|
||||||
|
<button type="button" class="btn btn-danger button-fixed button-sm"
|
||||||
|
th:attr="onclick=|confirm('Удалить запись?') && document.getElementById('remove-${manufacturer.id}').click()|">
|
||||||
|
<i class="fa fa-trash" aria-hidden="true"></i> Удалить
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<form th:action="@{/manufacturer/delete/{id}(id=${manufacturer.id})}" method="post">
|
||||||
|
<button th:id="'remove-' + ${manufacturer.id}" type="submit" style="display: none">
|
||||||
|
Удалить
|
||||||
|
</button>
|
||||||
|
</form>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
<form th:action="@{/product/filter(selected=${selected})}" enctype="manufacturerid/form-data" method="get">
|
||||||
|
<div class="input-group mb-3">
|
||||||
|
<select class="form-select" th:name="manufacturerid">
|
||||||
|
<option th:each="manufacturer, iterator: ${manufacturers}" th:value="${manufacturer.id}" th:text="${manufacturer.name}"></option>
|
||||||
|
</select>
|
||||||
|
<button class="btn btn-outline-secondary" type="submit">Добавить</button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
50
src/main/resources/templates/product-mtm.html
Normal file
50
src/main/resources/templates/product-mtm.html
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en"
|
||||||
|
xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout"
|
||||||
|
layout:decorate="~{default}">
|
||||||
|
<head>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div layout:fragment="content">
|
||||||
|
<div class="table-responsive">
|
||||||
|
<table class="table">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th scope="col">Название</th>
|
||||||
|
<th scope="col">Редактировать запись</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<tr th:each="pm, iterator: ${productmanufacturers}">
|
||||||
|
<td th:text="${pm.name}" style="width: 25%"/>
|
||||||
|
<td style="width: 10%">
|
||||||
|
<div class="btn-group" role="group" aria-label="Basic example">
|
||||||
|
<button type="button" class="btn btn-danger button-fixed button-sm"
|
||||||
|
th:attr="onclick=|confirm('Удалить запись?') && document.getElementById('remove-${pm.id}').click()|">
|
||||||
|
<i class="fa fa-trash" aria-hidden="true"></i> Удалить
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<form th:action="@{/product/{id}/manufacturers/{manufacturerid}(id=${product.id}, manufacturerid=${pm.id})}" method="post">
|
||||||
|
<button th:id="'remove-' + ${pm.id}" type="submit" style="display: none">
|
||||||
|
Удалить
|
||||||
|
</button>
|
||||||
|
</form>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
<form th:action="@{/product/{id}/manufacturers(id=${product.id})}" enctype="manufacturerid/form-data" method="post">
|
||||||
|
<div class="input-group mb-3">
|
||||||
|
<select class="form-select" th:name="manufacturerid">
|
||||||
|
<option th:each="manufacturer, iterator: ${manufacturers}" th:value="${manufacturer.id}" th:text="${manufacturer.name}"></option>
|
||||||
|
</select>
|
||||||
|
<button class="btn btn-outline-secondary" type="submit">Добавить</button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
<div>
|
||||||
|
<a class="btn btn-secondary" th:href="@{/product}">Закрыть</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
44
src/main/resources/templates/product-update.html
Normal file
44
src/main/resources/templates/product-update.html
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en"
|
||||||
|
xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout"
|
||||||
|
layout:decorate="~{default}" xmlns:th="http://www.w3.org/1999/xhtml">
|
||||||
|
<head>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div layout:fragment="content">
|
||||||
|
<div th:text="${errors}" class="margin-bottom alert-danger"></div>
|
||||||
|
<form action="#" th:action="@{/product/{id}(id=${id})}" th:object="${productDto}" method="post" enctype="multipart/form-data">
|
||||||
|
<div class="mb-3">
|
||||||
|
<label for="name" class="form-label">Название</label>
|
||||||
|
<input type="text" class="form-control" id="name" th:field="${productDto.name}" required="true">
|
||||||
|
</div>
|
||||||
|
<div class="mb-3">
|
||||||
|
<label for="price" class="form-label">Цена</label>
|
||||||
|
<input type="text" class="form-control" id="price" th:field="${productDto.price}" required="true">
|
||||||
|
</div>
|
||||||
|
<div class="mb-3">
|
||||||
|
<label for="photo" class="form-label">Фото</label>
|
||||||
|
<input type="file" class="form-control" id="photo" th:name="multipartFile" required="true">
|
||||||
|
<img th:src="${productDto.photo}" class="img-thumbnail mw-50 mh-50"/>
|
||||||
|
</div>
|
||||||
|
<label for="category" class="form-label">Категория</label>
|
||||||
|
<select id="category" class="form-control" th:name="categoryId">
|
||||||
|
<option th:each="category : ${categories}" th:value="${category.id}" th:text="${category.name}"></option>
|
||||||
|
</select>
|
||||||
|
<div class="mb-3">
|
||||||
|
<button type="submit" class="btn btn-success button-fixed">
|
||||||
|
<span th:if="${id == null}">
|
||||||
|
<i class="fa-solid fa-plus"></i> Добавить
|
||||||
|
</span>
|
||||||
|
<span th:if="${id != null}">
|
||||||
|
<i class="fa fa-pencil" aria-hidden="true"></i> Сохранить
|
||||||
|
</span>
|
||||||
|
</button>
|
||||||
|
<a class="btn btn-secondary button-fixed" th:href="@{/product}">
|
||||||
|
Назад
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
63
src/main/resources/templates/products.html
Normal file
63
src/main/resources/templates/products.html
Normal file
@ -0,0 +1,63 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en"
|
||||||
|
xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout"
|
||||||
|
layout:decorate="~{default}">
|
||||||
|
<head>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div layout:fragment="content">
|
||||||
|
<div>
|
||||||
|
<a class="btn btn-success button-fixed"
|
||||||
|
th:href="@{/product/update/}">
|
||||||
|
<i class="fa-solid fa-plus"></i> Добавить
|
||||||
|
</a>
|
||||||
|
<a class="btn btn-success button-fixed"
|
||||||
|
th:href="@{/product/filter/}">
|
||||||
|
<i class="fa-solid fa-plus"></i> Фильтр
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
<div class="table-responsive">
|
||||||
|
<table class="table">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th scope="col">Название</th>
|
||||||
|
<th scope="col">Цена</th>
|
||||||
|
<th scope="col">Фото</th>
|
||||||
|
<th scope="col">Категория</th>
|
||||||
|
<th scope="col">Редактировать запись</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<tr th:each="product, iterator: ${products}">
|
||||||
|
<td th:text="${product.name}" style="width: 25%"/>
|
||||||
|
<td th:text="${product.price}" style="width: 25%"/>
|
||||||
|
<td><img th:src="${product.photo}" class="img-thumbnail mw-50 mh-50"/></td>
|
||||||
|
<td th:text="${product.categoryName}" style="width: 25%"/>
|
||||||
|
<td style="width: 10%">
|
||||||
|
<div class="btn-group" role="group" aria-label="Basic example">
|
||||||
|
<a class="btn btn-warning button-fixed button-sm"
|
||||||
|
th:href="@{/product/update/{id}(id=${product.id})}">
|
||||||
|
<i class="fa fa-pencil" aria-hidden="true"></i> Изменить
|
||||||
|
</a>
|
||||||
|
<button type="button" class="btn btn-danger button-fixed button-sm"
|
||||||
|
th:attr="onclick=|confirm('Удалить запись?') && document.getElementById('remove-${product.id}').click()|">
|
||||||
|
<i class="fa fa-trash" aria-hidden="true"></i> Удалить
|
||||||
|
</button>
|
||||||
|
<a class="btn btn-primary button-fixed button-sm"
|
||||||
|
th:href="@{/product/{id}/manufacturers(id=${product.id})}">
|
||||||
|
<i class="fa fa-pencil" aria-hidden="true"></i> Производители
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
<form th:action="@{/product/delete/{id}(id=${product.id})}" method="post">
|
||||||
|
<button th:id="'remove-' + ${product.id}" type="submit" style="display: none">
|
||||||
|
Удалить
|
||||||
|
</button>
|
||||||
|
</form>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
29
src/main/resources/templates/signup.html
Normal file
29
src/main/resources/templates/signup.html
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en"
|
||||||
|
xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout"
|
||||||
|
xmlns:th="http://www.thymeleaf.org"
|
||||||
|
layout:decorate="~{default}">
|
||||||
|
<body>
|
||||||
|
<div class="container container-padding mt-3" layout:fragment="content">
|
||||||
|
<div th:if="${errors}" th:text="${errors}" class="margin-bottom alert alert-danger"></div>
|
||||||
|
<form action="#" th:action="@{/signup}" th:object="${UserDTO}" method="post">
|
||||||
|
<div class="mb-3">
|
||||||
|
<input type="text" class="form-control" th:field="${UserDTO.login}"
|
||||||
|
placeholder="Логин" required="true" autofocus="true" maxlength="64"/>
|
||||||
|
</div>
|
||||||
|
<div class="mb-3">
|
||||||
|
<input type="password" class="form-control" th:field="${UserDTO.password}"
|
||||||
|
placeholder="Пароль" required="true" minlength="6" maxlength="64"/>
|
||||||
|
</div>
|
||||||
|
<div class="mb-3">
|
||||||
|
<input type="password" class="form-control" th:field="${UserDTO.passwordConfirm}"
|
||||||
|
placeholder="Пароль (подтверждение)" required="true" minlength="6" maxlength="64"/>
|
||||||
|
</div>
|
||||||
|
<div class="mx-4">
|
||||||
|
<button type="submit" class="btn btn-success button-fixed">Создать</button>
|
||||||
|
<a class="btn btn-primary button-fixed" href="/login">Назад</a>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
38
src/main/resources/templates/users.html
Normal file
38
src/main/resources/templates/users.html
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en"
|
||||||
|
xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout"
|
||||||
|
xmlns:th="http://www.thymeleaf.org"
|
||||||
|
layout:decorate="~{default}">
|
||||||
|
<body>
|
||||||
|
<div class="container" layout:fragment="content">
|
||||||
|
<div class="table-responsive">
|
||||||
|
<table class="table">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th scope="col">#</th>
|
||||||
|
<th scope="col">ID</th>
|
||||||
|
<th scope="col">Логин</th>
|
||||||
|
<th scope="col">Роль</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<tr th:each="user, iterator: ${users}">
|
||||||
|
<th scope="row" th:text="${iterator.index} + 1"></th>
|
||||||
|
<td th:text="${user.id}"></td>
|
||||||
|
<td th:text="${user.login}" style="width: 60%"></td>
|
||||||
|
<td th:text="${user.role}" style="width: 20%"></td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
<div th:if="${totalPages > 0}" class="pagination">
|
||||||
|
<span style="float: left; padding: 5px 5px;">Страницы:</span>
|
||||||
|
<a th:each="page : ${pages}"
|
||||||
|
th:href="@{/users(page=${page}, size=${users.size})}"
|
||||||
|
th:text="${page}"
|
||||||
|
th:class="${page == users.number + 1} ? active">
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
63
src/test/java/ru/ulstu/is/sbapp/JpaCategoryTests.java
Normal file
63
src/test/java/ru/ulstu/is/sbapp/JpaCategoryTests.java
Normal file
@ -0,0 +1,63 @@
|
|||||||
|
package ru.ulstu.is.sbapp;
|
||||||
|
|
||||||
|
import org.junit.jupiter.api.Assertions;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.boot.test.context.SpringBootTest;
|
||||||
|
import ru.ulstu.is.sbapp.HardwareShop.models.Category;
|
||||||
|
import ru.ulstu.is.sbapp.HardwareShop.services.CategoryService;
|
||||||
|
|
||||||
|
import javax.persistence.EntityNotFoundException;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
@SpringBootTest
|
||||||
|
public class JpaCategoryTests {
|
||||||
|
private static final Logger log = LoggerFactory.getLogger(JpaCategoryTests.class);
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private CategoryService categoryService;
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testCategoryCreate() {
|
||||||
|
categoryService.deleteAllCategories();
|
||||||
|
final Category category = categoryService.addCategory("Электроника");
|
||||||
|
log.info(category.toString());
|
||||||
|
Assertions.assertNotNull(category.getId());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testCategoryRead() {
|
||||||
|
categoryService.deleteAllCategories();
|
||||||
|
final Category category = categoryService.addCategory("Электроника");
|
||||||
|
log.info(category.toString());
|
||||||
|
final Category findCategory = categoryService.findCategory(category.getId());
|
||||||
|
log.info(findCategory.toString());
|
||||||
|
Assertions.assertEquals(category, findCategory);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testCategoryReadNotFound() {
|
||||||
|
categoryService.deleteAllCategories();
|
||||||
|
Assertions.assertThrows(EntityNotFoundException.class, () -> categoryService.findCategory(-1L));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testCategoryReadAll() {
|
||||||
|
categoryService.deleteAllCategories();
|
||||||
|
categoryService.addCategory("Электроника");
|
||||||
|
categoryService.addCategory("Электроника 2");
|
||||||
|
final List<Category> categoryList = categoryService.findAllCategories();
|
||||||
|
log.info(categoryList.toString());
|
||||||
|
Assertions.assertEquals(categoryList.size(), 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testCategoryReadAllEmpty() {
|
||||||
|
categoryService.deleteAllCategories();
|
||||||
|
final List<Category> categoryList = categoryService.findAllCategories();
|
||||||
|
log.info(categoryList.toString());
|
||||||
|
Assertions.assertEquals(categoryList.size(), 0);
|
||||||
|
}
|
||||||
|
}
|
63
src/test/java/ru/ulstu/is/sbapp/JpaManufacturerTests.java
Normal file
63
src/test/java/ru/ulstu/is/sbapp/JpaManufacturerTests.java
Normal file
@ -0,0 +1,63 @@
|
|||||||
|
package ru.ulstu.is.sbapp;
|
||||||
|
|
||||||
|
import org.junit.jupiter.api.Assertions;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.boot.test.context.SpringBootTest;
|
||||||
|
import ru.ulstu.is.sbapp.HardwareShop.models.Manufacturer;
|
||||||
|
import ru.ulstu.is.sbapp.HardwareShop.services.ManufacturerService;
|
||||||
|
|
||||||
|
import javax.persistence.EntityNotFoundException;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
@SpringBootTest
|
||||||
|
public class JpaManufacturerTests {
|
||||||
|
private static final Logger log = LoggerFactory.getLogger(JpaManufacturerTests.class);
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private ManufacturerService manufacturerService;
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testManufacturerCreate() {
|
||||||
|
manufacturerService.deleteAllManufacturers();
|
||||||
|
final Manufacturer manufacturer = manufacturerService.addManufacturer("Производитель 1", "ул.Камышинская д.107");
|
||||||
|
log.info(manufacturer.toString());
|
||||||
|
Assertions.assertNotNull(manufacturer.getId());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testManufacturerRead() {
|
||||||
|
manufacturerService.deleteAllManufacturers();
|
||||||
|
final Manufacturer manufacturer = manufacturerService.addManufacturer("Производитель 1", "ул.Камышинская д.107");
|
||||||
|
log.info(manufacturer.toString());
|
||||||
|
final Manufacturer findManufacturer = manufacturerService.findManufacturer(manufacturer.getId());
|
||||||
|
log.info(findManufacturer.toString());
|
||||||
|
Assertions.assertEquals(manufacturer, findManufacturer);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testManufacturerReadNotFound() {
|
||||||
|
manufacturerService.deleteAllManufacturers();
|
||||||
|
Assertions.assertThrows(EntityNotFoundException.class, () -> manufacturerService.findManufacturer(-1L));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testManufacturerReadAll() {
|
||||||
|
manufacturerService.deleteAllManufacturers();
|
||||||
|
manufacturerService.addManufacturer("Производитель 1", "ул.Камышинская д.107");
|
||||||
|
manufacturerService.addManufacturer("Производитель 2", "ул.Камышинская д.108");
|
||||||
|
final List<Manufacturer> manufacturerList = manufacturerService.findAllManufacturers();
|
||||||
|
log.info(manufacturerList.toString());
|
||||||
|
Assertions.assertEquals(manufacturerList.size(), 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testManufacturerReadAllEmpty() {
|
||||||
|
manufacturerService.deleteAllManufacturers();
|
||||||
|
final List<Manufacturer> manufacturerList = manufacturerService.findAllManufacturers();
|
||||||
|
log.info(manufacturerList.toString());
|
||||||
|
Assertions.assertEquals(manufacturerList.size(), 0);
|
||||||
|
}
|
||||||
|
}
|
115
src/test/java/ru/ulstu/is/sbapp/JpaProductTests.java
Normal file
115
src/test/java/ru/ulstu/is/sbapp/JpaProductTests.java
Normal file
@ -0,0 +1,115 @@
|
|||||||
|
package ru.ulstu.is.sbapp;
|
||||||
|
|
||||||
|
import org.junit.jupiter.api.Assertions;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.boot.test.context.FilteredClassLoader;
|
||||||
|
import org.springframework.boot.test.context.SpringBootTest;
|
||||||
|
import org.springframework.mock.web.MockMultipartFile;
|
||||||
|
import org.springframework.web.multipart.MultipartFile;
|
||||||
|
import ru.ulstu.is.sbapp.HardwareShop.models.Category;
|
||||||
|
import ru.ulstu.is.sbapp.HardwareShop.services.CategoryService;
|
||||||
|
import ru.ulstu.is.sbapp.HardwareShop.models.Product;
|
||||||
|
import ru.ulstu.is.sbapp.HardwareShop.services.ProductService;
|
||||||
|
|
||||||
|
import javax.persistence.EntityNotFoundException;
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.FileInputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.nio.file.Files;
|
||||||
|
import java.nio.file.Path;
|
||||||
|
import java.util.Base64;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
@SpringBootTest
|
||||||
|
public class JpaProductTests {
|
||||||
|
private static final Logger log = LoggerFactory.getLogger(JpaProductTests.class);
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private ProductService productService;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private CategoryService categoryService;
|
||||||
|
|
||||||
|
private MultipartFile ConvertFile() throws IOException {
|
||||||
|
File file = new File("C:\\Users\\aleyc\\IdeaProjects\\IP_Aleikin_PIbd-22\\src\\test\\java\\ru\\ulstu\\is\\sbapp\\cart.png");
|
||||||
|
String name = "file";
|
||||||
|
String originalFileName = file.getName();
|
||||||
|
String contentType = "image/jpeg";
|
||||||
|
byte[] content = new byte[(int) file.length()];
|
||||||
|
FileInputStream inputStream = new FileInputStream(file);
|
||||||
|
inputStream.read(content);
|
||||||
|
MultipartFile multipartFile = new MockMultipartFile(name, originalFileName, contentType, content);
|
||||||
|
return multipartFile;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testProductCreate() throws Exception {
|
||||||
|
MultipartFile localFile = ConvertFile();
|
||||||
|
String strImage;
|
||||||
|
strImage = ("data:" + localFile.getContentType() + ";base64," + Base64.getEncoder().encodeToString(localFile.getBytes()));
|
||||||
|
productService.deleteAllProducts();
|
||||||
|
final Product product = productService.addProduct(15000, strImage.getBytes(), "1412");
|
||||||
|
log.info(product.toString());
|
||||||
|
Assertions.assertNotNull(product.getId());
|
||||||
|
Assertions.assertNotNull(product.getPhoto());
|
||||||
|
}
|
||||||
|
|
||||||
|
/*@Test
|
||||||
|
void testProductRead() {
|
||||||
|
productService.deleteAllProducts();
|
||||||
|
final Product product = productService.addProduct(15000);
|
||||||
|
log.info(product.toString());
|
||||||
|
final Product findProduct = productService.findProduct(product.getId());
|
||||||
|
log.info(findProduct.toString());
|
||||||
|
Assertions.assertEquals(product, findProduct);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testProductReadNotFound() {
|
||||||
|
productService.deleteAllProducts();
|
||||||
|
Assertions.assertThrows(EntityNotFoundException.class, () -> productService.findProduct(-1L));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testProductReadAll() {
|
||||||
|
productService.deleteAllProducts();
|
||||||
|
productService.addProduct(15000);
|
||||||
|
productService.addProduct(20000);
|
||||||
|
final List<Product> productList = productService.findAllProducts();
|
||||||
|
log.info(productList.toString());
|
||||||
|
Assertions.assertEquals(productList.size(), 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testProductReadAllEmpty() {
|
||||||
|
productService.deleteAllProducts();
|
||||||
|
final List<Product> productList = productService.findAllProducts();
|
||||||
|
log.info(productList.toString());
|
||||||
|
Assertions.assertEquals(productList.size(), 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testProductUpdate() {
|
||||||
|
productService.deleteAllProducts();
|
||||||
|
final Product product = productService.addProduct(15000);
|
||||||
|
log.info(product.toString());
|
||||||
|
productService.updateProduct(product.getId(), 20000);
|
||||||
|
log.info(product.toString());
|
||||||
|
Assertions.assertEquals(product.getPrice(), 20000);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testProductAddCategory() {
|
||||||
|
productService.deleteAllProducts();
|
||||||
|
final Product product = productService.addProduct(15000);
|
||||||
|
final Category category = categoryService.addCategory("Электроника");
|
||||||
|
product.addCategory(category);
|
||||||
|
log.info(product.toString() + product.getCategory().getName());
|
||||||
|
Assertions.assertEquals(product.getCategory().getName(), "Электроника");
|
||||||
|
productService.deleteAllProducts();
|
||||||
|
categoryService.deleteAllCategories();
|
||||||
|
}*/
|
||||||
|
}
|
@ -1,13 +1,93 @@
|
|||||||
|
/*
|
||||||
package ru.ulstu.is.sbapp;
|
package ru.ulstu.is.sbapp;
|
||||||
|
|
||||||
|
import org.junit.jupiter.api.Assertions;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
|
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.boot.test.context.SpringBootTest;
|
import org.springframework.boot.test.context.SpringBootTest;
|
||||||
|
import ru.ulstu.is.sbapp.calc.service.CalculatorService;
|
||||||
|
|
||||||
@SpringBootTest
|
@SpringBootTest
|
||||||
class SbappApplicationTests {
|
class SbappApplicationTests {
|
||||||
|
@Autowired
|
||||||
|
CalculatorService calculatorService;
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void contextLoads() {
|
void IntSum() {
|
||||||
|
final String res = calculatorService.getSum(3, 6, "int").toString();
|
||||||
|
Assertions.assertEquals("9", res);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void IntDiff() {
|
||||||
|
final String res = calculatorService.getDiff(15, 3, "int").toString();
|
||||||
|
Assertions.assertEquals("12", res);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void IntMul() {
|
||||||
|
final String res = calculatorService.getMul(12, 3, "int").toString();
|
||||||
|
Assertions.assertEquals("36", res);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void IntDiv() {
|
||||||
|
final String res = calculatorService.getDiv(12, 3, "int").toString();
|
||||||
|
Assertions.assertEquals("4", res);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void StringSum() {
|
||||||
|
final String res = calculatorService.getSum("abc", 6, "string").toString();
|
||||||
|
Assertions.assertEquals("abc6", res);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void StringDiff() {
|
||||||
|
final String res = calculatorService.getDiff("abc", 2, "string").toString();
|
||||||
|
Assertions.assertEquals("a", res);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void StringMul() {
|
||||||
|
final String res = calculatorService.getMul("abc", 3, "string").toString();
|
||||||
|
Assertions.assertEquals("abcabcabc", res);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void StringDiv() {
|
||||||
|
final String res = calculatorService.getDiv("abc", 3, "string").toString();
|
||||||
|
Assertions.assertEquals("a", res);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void FloatArraySum() {
|
||||||
|
final String res = calculatorService.getSum("3.2;5.6", 3, "floatArray").toString();
|
||||||
|
Assertions.assertEquals("[6.2, 8.6]", res);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void FloatArrayDiff() {
|
||||||
|
final String res = calculatorService.getDiff("3.2;5.6", 2, "floatArray").toString();
|
||||||
|
Assertions.assertEquals("[1.2, 3.6]", res);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void FloatArrayMul() {
|
||||||
|
final String res = calculatorService.getMul("3.2;5.6", 2, "floatArray").toString();
|
||||||
|
Assertions.assertEquals("[6.4, 11.2]", res);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void FloatArrayDiv() {
|
||||||
|
final String res = calculatorService.getDiv("3.2;5.6", 2, "floatArray").toString();
|
||||||
|
Assertions.assertEquals("[1.6, 2.8]", res);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testErrorErrorWired() {
|
||||||
|
Assertions.assertThrows(NoSuchBeanDefinitionException.class, () -> calculatorService.getSum(1, 2, "date"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
BIN
src/test/java/ru/ulstu/is/sbapp/cart.png
Normal file
BIN
src/test/java/ru/ulstu/is/sbapp/cart.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 17 KiB |
Loading…
Reference in New Issue
Block a user