Compare commits
20 Commits
main
...
LabWork06_
Author | SHA1 | Date | |
---|---|---|---|
a68a8ca1b1 | |||
0c5ae531f6 | |||
1187149b02 | |||
fb25e23439 | |||
a84e05540a | |||
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/
|
||||
|
||||
### IntelliJ IDEA ###
|
||||
*.png
|
||||
*.jpeg
|
||||
*.webp
|
||||
.idea
|
||||
*.iws
|
||||
*.iml
|
||||
@ -35,3 +38,4 @@ out/
|
||||
|
||||
### VS Code ###
|
||||
.vscode/
|
||||
/frontend/node_modules/
|
||||
|
20
build.gradle
20
build.gradle
@ -6,14 +6,32 @@ plugins {
|
||||
|
||||
group = 'ru.ulstu.is'
|
||||
version = '0.0.1-SNAPSHOT'
|
||||
sourceCompatibility = '17'
|
||||
sourceCompatibility = '19'
|
||||
|
||||
repositories {
|
||||
mavenCentral()
|
||||
}
|
||||
|
||||
dependencies {
|
||||
annotationProcessor "org.springframework.boot:spring-boot-configuration-processor"
|
||||
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.springframework.boot:spring-boot-devtools'
|
||||
implementation 'org.thymeleaf.extras:thymeleaf-extras-springsecurity5'
|
||||
implementation 'nz.net.ultraq.thymeleaf:thymeleaf-layout-dialect'
|
||||
implementation 'jakarta.validation:jakarta.validation-api:3.0.0'
|
||||
implementation 'org.webjars:bootstrap:5.1.3'
|
||||
implementation 'org.webjars:jquery:3.6.0'
|
||||
implementation 'org.webjars:font-awesome:6.1.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'
|
||||
}
|
||||
|
||||
|
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%;
|
||||
}
|
52
frontend/spa-vue/src/components/Header.vue
Normal file
52
frontend/spa-vue/src/components/Header.vue
Normal file
@ -0,0 +1,52 @@
|
||||
<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>
|
||||
<router-link to="/users" class="nav-link">Users</router-link>
|
||||
<button type="button" class="btn btn-secondary" @click="logout()">Разлогиниться</button>
|
||||
</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 {
|
||||
methods:
|
||||
{
|
||||
logout(){
|
||||
localStorage.removeItem("token");
|
||||
localStorage.removeItem("user");
|
||||
localStorage.removeItem("role");
|
||||
this.$router.push('/login');
|
||||
},
|
||||
}
|
||||
|
||||
}
|
||||
</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;
|
||||
}
|
||||
}
|
198
frontend/spa-vue/src/pages/categories.vue
Normal file
198
frontend/spa-vue/src/pages/categories.vue
Normal file
@ -0,0 +1,198 @@
|
||||
<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/api/",
|
||||
category: new Category(),
|
||||
editedCategory: new Category(),
|
||||
products: [],
|
||||
postParams: {
|
||||
method:"POST",
|
||||
headers:{
|
||||
"Content-Type":"application/json",
|
||||
"Authorization": "Bearer " + localStorage.getItem("token"),
|
||||
}
|
||||
},
|
||||
putParams: {
|
||||
method:"PUT",
|
||||
headers:{
|
||||
"Content-Type":"application/json",
|
||||
"Authorization": "Bearer " + localStorage.getItem("token"),
|
||||
},
|
||||
},
|
||||
delParams: {
|
||||
method:"DELETE",
|
||||
headers:{
|
||||
"Content-Type":"application/json",
|
||||
"Authorization": "Bearer " + localStorage.getItem("token"),
|
||||
}
|
||||
},
|
||||
getParams: {
|
||||
method:"GET",
|
||||
headers:{
|
||||
"Authorization": "Bearer " + localStorage.getItem("token"),
|
||||
}
|
||||
},
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
getCategories(){
|
||||
axios.get(this.URL + "category", this.getParams)
|
||||
.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, this.postParams)
|
||||
.then(() => {
|
||||
this.getCategories();
|
||||
this.closeModal();
|
||||
})
|
||||
.catch((error) => {
|
||||
console.log(error);
|
||||
});
|
||||
},
|
||||
deleteCategory(id){
|
||||
axios.delete(this.URL + `category/${id}`, this.delParams)
|
||||
.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, this.putParams)
|
||||
.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>
|
9
frontend/spa-vue/src/pages/error.vue
Normal file
9
frontend/spa-vue/src/pages/error.vue
Normal file
@ -0,0 +1,9 @@
|
||||
<template>
|
||||
<div>
|
||||
<a>Ошибка доступа</a>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
</script>
|
||||
<style>
|
||||
</style>
|
83
frontend/spa-vue/src/pages/login.vue
Normal file
83
frontend/spa-vue/src/pages/login.vue
Normal file
@ -0,0 +1,83 @@
|
||||
<template>
|
||||
<div>
|
||||
<div class="login">
|
||||
<form @submit.prevent class="login__form" action="/">
|
||||
<div class="form__login">
|
||||
<input class="login-input form-control"
|
||||
id="login" v-model="newUser.login"
|
||||
validate="false"
|
||||
placeholder="Логин"
|
||||
type="text"
|
||||
name="Логин">
|
||||
</div>
|
||||
<div class="form__password">
|
||||
<input class="password-input form-control"
|
||||
id="password" v-model="newUser.password"
|
||||
validate="false"
|
||||
placeholder="Пароль"
|
||||
type="password"
|
||||
name="Пароль">
|
||||
</div>
|
||||
<div class="login__buttons mt-5">
|
||||
<button type="submit" class="login__confirm" id="login__confirm" @click="login">Войти</button>
|
||||
<a class="login__registration" href="registration">Регистрация</a>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
import axios from 'axios';
|
||||
export default {
|
||||
mounted(){
|
||||
|
||||
},
|
||||
data(){
|
||||
return{
|
||||
newUser: new Object,
|
||||
token: '',
|
||||
user: '',
|
||||
role: '',
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
async login() {
|
||||
await this.getRole()
|
||||
try {
|
||||
const response = await axios.post(`http://localhost:8080/jwt/login`, this.newUser);
|
||||
if (response.status === 200) {
|
||||
localStorage.setItem("token", response.data);
|
||||
localStorage.setItem("user", this.newUser.login);
|
||||
this.token = response.data;
|
||||
this.user = this.newUser.login;
|
||||
this.$emit('login', {
|
||||
token: this.token,
|
||||
user: this.newUser.login,
|
||||
role: this.role,
|
||||
});
|
||||
this.$router.push("/authors")
|
||||
} else {
|
||||
localStorage.removeItem("token");
|
||||
localStorage.removeItem("user");
|
||||
localStorage.removeItem("role");
|
||||
}
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
}
|
||||
},
|
||||
|
||||
async getRole(){
|
||||
await axios.get(`http://localhost:8080/who_am_i?userLogin=${this.newUser.login}`)
|
||||
.then((response) => {
|
||||
localStorage.setItem("role", response.data)
|
||||
this.role = response.data
|
||||
})
|
||||
.catch(error => {
|
||||
console.log(error);
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
<style>
|
||||
</style>
|
161
frontend/spa-vue/src/pages/manufacturers.vue
Normal file
161
frontend/spa-vue/src/pages/manufacturers.vue
Normal file
@ -0,0 +1,161 @@
|
||||
<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/api/",
|
||||
manufacturer: new Manufacturer(),
|
||||
postParams: {
|
||||
method:"POST",
|
||||
headers:{
|
||||
"Content-Type":"application/json",
|
||||
"Authorization": "Bearer " + localStorage.getItem("token"),
|
||||
}
|
||||
},
|
||||
putParams: {
|
||||
method:"PUT",
|
||||
headers:{
|
||||
"Content-Type":"application/json",
|
||||
"Authorization": "Bearer " + localStorage.getItem("token"),
|
||||
},
|
||||
},
|
||||
delParams: {
|
||||
method:"DELETE",
|
||||
headers:{
|
||||
"Content-Type":"application/json",
|
||||
"Authorization": "Bearer " + localStorage.getItem("token"),
|
||||
}
|
||||
},
|
||||
getParams: {
|
||||
method:"GET",
|
||||
headers:{
|
||||
"Authorization": "Bearer " + localStorage.getItem("token"),
|
||||
}
|
||||
},
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
getManufacturers(){
|
||||
axios.get(this.URL + "manufacturer", this.getParams)
|
||||
.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, this.postParams)
|
||||
.then(() => {
|
||||
this.getManufacturers();
|
||||
this.closeModal();
|
||||
})
|
||||
.catch(error => {
|
||||
console.log(error);
|
||||
});
|
||||
},
|
||||
deleteManufacturer(id){
|
||||
axios.delete(this.URL + `manufacturer/${id}`, this.delParams)
|
||||
.then(() =>{
|
||||
this.getManufacturers();
|
||||
})
|
||||
},
|
||||
editManufacturer(manufacturer){
|
||||
axios.put(this.URL + `manufacturer/${manufacturer.id}`, manufacturer, this.putParams)
|
||||
.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>
|
301
frontend/spa-vue/src/pages/products.vue
Normal file
301
frontend/spa-vue/src/pages/products.vue
Normal file
@ -0,0 +1,301 @@
|
||||
<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/api/",
|
||||
product: new Product(),
|
||||
editedProduct: new Product(),
|
||||
postParams: {
|
||||
method:"POST",
|
||||
headers:{
|
||||
"Content-Type":"application/json",
|
||||
"Authorization": "Bearer " + localStorage.getItem("token"),
|
||||
}
|
||||
},
|
||||
putParams: {
|
||||
method:"PUT",
|
||||
headers:{
|
||||
"Content-Type":"application/json",
|
||||
"Authorization": "Bearer " + localStorage.getItem("token"),
|
||||
},
|
||||
},
|
||||
delParams: {
|
||||
method:"DELETE",
|
||||
headers:{
|
||||
"Content-Type":"application/json",
|
||||
"Authorization": "Bearer " + localStorage.getItem("token"),
|
||||
}
|
||||
},
|
||||
getParams: {
|
||||
method:"GET",
|
||||
headers:{
|
||||
"Authorization": "Bearer " + localStorage.getItem("token"),
|
||||
}
|
||||
},
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
getProducts(){
|
||||
axios.get(this.URL + "product", this.getParams)
|
||||
.then(response => {
|
||||
this.products = response.data;
|
||||
console.log(response.data);
|
||||
})
|
||||
.catch(error => {
|
||||
console.log(error);
|
||||
});
|
||||
},
|
||||
getCategories(){
|
||||
axios.get(this.URL + "category", this.getParams)
|
||||
.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, this.postParams)
|
||||
.then(() => {
|
||||
this.getProducts();
|
||||
this.closeModal();
|
||||
})
|
||||
.catch(error => {
|
||||
console.log(error);
|
||||
});
|
||||
},
|
||||
deleteProduct(id){
|
||||
axios.delete(this.URL + `product/${id}`, this.delParams)
|
||||
.then(() =>{
|
||||
this.getProducts();
|
||||
})
|
||||
},
|
||||
async editProduct(product){
|
||||
if(product.photo === undefined) await this.toBase64();
|
||||
console.log(product);
|
||||
axios.put(this.URL + `product/${product.id}`, product, this.putParams)
|
||||
.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`, this.getParams)
|
||||
.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}`, this.postParams)
|
||||
.then(() => {
|
||||
this.getProductManufacturers();
|
||||
})
|
||||
.catch(error => {
|
||||
console.log(error);
|
||||
});
|
||||
},
|
||||
removeManufacturer(id){
|
||||
axios.delete(this.URL + `product/${this.product.id}/Manufacturer/${id}`, this.delParams)
|
||||
.then(() =>{
|
||||
this.getProductManufacturers();
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
<style>
|
||||
|
||||
</style>
|
46
frontend/spa-vue/src/pages/registration.vue
Normal file
46
frontend/spa-vue/src/pages/registration.vue
Normal file
@ -0,0 +1,46 @@
|
||||
<template>
|
||||
<div class="registration">
|
||||
<form @submit.prevent="registration" class="registration__form" action="#">
|
||||
<div class="form__login">
|
||||
<input class="login-input form-control" v-model="login" id="login" required="" validate="false" placeholder="Логин" type="text" name="Логин">
|
||||
</div>
|
||||
<div class="form__password">
|
||||
<input class="password-input form-control" v-model="password" id="password" required="" validate="false" placeholder="Пароль" type="password" name="Пароль">
|
||||
</div>
|
||||
<div class="registration__buttons">
|
||||
<button class="registration__confirm" id="reg_btn" type="submit">Зарегестрироваться</button><a class="registration__login" href="/login">Уже есть аккаунт</a>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
import axios from 'axios';
|
||||
export default {
|
||||
data(){
|
||||
return{
|
||||
login: "",
|
||||
password: "",
|
||||
}
|
||||
},
|
||||
methods:{
|
||||
registration(){
|
||||
// Отправляем данные формы на сервер
|
||||
let user = {
|
||||
login: this.login,
|
||||
password: this.password,
|
||||
passwordConfirm: this.password
|
||||
}
|
||||
axios.post(`http://localhost:8080/sign_up`, user)
|
||||
.then((response) => {
|
||||
if (response.status === 200) {
|
||||
alert(response.data);
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
console.log(error);
|
||||
});
|
||||
this.$router.push('/login');
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
52
frontend/spa-vue/src/pages/users.vue
Normal file
52
frontend/spa-vue/src/pages/users.vue
Normal file
@ -0,0 +1,52 @@
|
||||
<template >
|
||||
<table class="table table-striped table-bordered table-hover">
|
||||
<thead>
|
||||
<tr>
|
||||
<th scope="col">ID</th>
|
||||
<th scope="col">Логин</th>
|
||||
<th scope="col">Роль</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr v-for="(user, index) in users" :key="index">
|
||||
<td>{{ user.id }}</td>
|
||||
<td>{{ user.login }}</td>
|
||||
<td>{{ user.role }}</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</template>
|
||||
<script>
|
||||
import axios from 'axios'
|
||||
export default {
|
||||
data(){
|
||||
return{
|
||||
users: []
|
||||
}
|
||||
},
|
||||
created(){
|
||||
const getTokenForHeader = function () {
|
||||
return "Bearer " + localStorage.getItem("token");
|
||||
}
|
||||
const requestParams = {
|
||||
method: "GET",
|
||||
headers: {
|
||||
"Authorization": getTokenForHeader(),
|
||||
}
|
||||
};
|
||||
axios.get(`http://localhost:8080`, requestParams)
|
||||
.then(response => {
|
||||
this.users = response.data;
|
||||
})
|
||||
.catch(error => {
|
||||
console.log(error);
|
||||
});
|
||||
},
|
||||
methods:{
|
||||
|
||||
}
|
||||
}
|
||||
</script>
|
||||
<style >
|
||||
|
||||
</style>
|
44
frontend/spa-vue/src/routes.js
Normal file
44
frontend/spa-vue/src/routes.js
Normal file
@ -0,0 +1,44 @@
|
||||
import manufacturers from './pages/manufacturers.vue'
|
||||
import categories from './pages/categories.vue'
|
||||
import products from './pages/products.vue'
|
||||
import users from "./pages/users.vue";
|
||||
import login from "./pages/login.vue";
|
||||
import registration from "./pages/registration.vue";
|
||||
import Error from "./pages/Error.vue";
|
||||
import {createRouter, createWebHistory} from "vue-router"
|
||||
|
||||
const routes = [
|
||||
{path: '/manufacturer', component: manufacturers, meta: { requiresAuth: true }},
|
||||
{path: '/category', component: categories, meta: { requiresAuth: true }},
|
||||
{path: '/product', component: products, meta:{ requiresAuth: true }},
|
||||
{path: "/users", component: users, meta: { requiresAuth: true, requiresAdmin: true }},
|
||||
{path: "/login", component: login},
|
||||
{path: "/registration", component: registration},
|
||||
{path: "/error", component: Error, meta: { requiresAuth: true }},
|
||||
]
|
||||
|
||||
const router = createRouter({
|
||||
history: createWebHistory(),
|
||||
linkActiveClass: 'active',
|
||||
routes
|
||||
})
|
||||
|
||||
router.beforeEach((to, from, next) => {
|
||||
const isAuthenticated = localStorage.getItem("token");
|
||||
if (to.matched.some((route) => route.meta.requiresAuth)) {
|
||||
if (!isAuthenticated) {
|
||||
next("/login");
|
||||
return;
|
||||
}
|
||||
}
|
||||
const isAdmin = localStorage.getItem("role") === "ADMIN";
|
||||
if (to.matched.some((route) => route.meta.requiresAdmin)) {
|
||||
if (!isAdmin) {
|
||||
next("/error");
|
||||
return;
|
||||
}
|
||||
}
|
||||
next();
|
||||
});
|
||||
|
||||
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/api';
|
||||
|
||||
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,145 @@
|
||||
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(Model model, @RequestParam(name = "selected", required = false)List<Long> selected) {
|
||||
if(selected == null) {
|
||||
model.addAttribute("manufacturers", manufacturerService.findAllManufacturers()
|
||||
.stream()
|
||||
.map(ManufacturerDTO::new)
|
||||
.toList());
|
||||
model.addAttribute("selected", selected);
|
||||
return "product-filter";
|
||||
}
|
||||
|
||||
List<ProductDTO> productDTOS;
|
||||
List<ManufacturerDTO> manufacturerDTOS = new ArrayList<>();
|
||||
|
||||
for(int i = 0; i < selected.size(); ++i) {
|
||||
manufacturerDTOS.add(new ManufacturerDTO(manufacturerService.findManufacturer(selected.get(i))));
|
||||
}
|
||||
productDTOS = productService.getProductsFilter(manufacturerDTOS)
|
||||
.stream()
|
||||
.map(ProductDTO::new)
|
||||
.toList();
|
||||
|
||||
model.addAttribute("products", productDTOS);
|
||||
return "products";
|
||||
}
|
||||
|
||||
@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,64 @@
|
||||
package ru.ulstu.is.sbapp.HardwareShop.controller;
|
||||
|
||||
import jakarta.validation.Valid;
|
||||
import jakarta.validation.ValidationException;
|
||||
import org.springframework.security.access.annotation.Secured;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
import ru.ulstu.is.sbapp.HardwareShop.models.User;
|
||||
import ru.ulstu.is.sbapp.HardwareShop.models.UserRole;
|
||||
import ru.ulstu.is.sbapp.HardwareShop.services.UserService;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@RestController
|
||||
public class UserController {
|
||||
public static final String URL_LOGIN = "/jwt/login";
|
||||
public static final String URL_SIGN_UP = "/sign_up";
|
||||
public static final String URL_WHO_AM_I = "/who_am_i";
|
||||
private final UserService userService;
|
||||
|
||||
public UserController(UserService userService) {
|
||||
this.userService = userService;
|
||||
}
|
||||
@PostMapping(URL_LOGIN)
|
||||
public String login(@RequestBody @Valid UserDto userDto) {
|
||||
return userService.loginAndGetToken(userDto);
|
||||
}
|
||||
@GetMapping(URL_WHO_AM_I)
|
||||
public String role(@RequestParam("userLogin") String userLogin) {
|
||||
return userService.findByLogin(userLogin).getRole().name();
|
||||
}
|
||||
@PostMapping(URL_SIGN_UP)
|
||||
public String signUp(@RequestBody @Valid UserSignUpDTO userSignupDto) {
|
||||
try {
|
||||
final User user = userService.addUser(userSignupDto.getLogin(),
|
||||
userSignupDto.getPassword(), userSignupDto.getPasswordConfirm(), UserRole.USER);
|
||||
return "created " + user.getLogin();
|
||||
} catch (ValidationException e) {
|
||||
return e.getMessage();
|
||||
}
|
||||
}
|
||||
@GetMapping("/{id}")
|
||||
@Secured({UserRole.AsString.ADMIN})
|
||||
public UserDto getUser(@PathVariable Long id) {
|
||||
return new UserDto(userService.findUser(id));
|
||||
}
|
||||
@GetMapping("/")
|
||||
@Secured({UserRole.AsString.ADMIN})
|
||||
public List<UserDto> getUsers() {
|
||||
return userService.findAllUsers().stream()
|
||||
.map(UserDto::new)
|
||||
.toList();
|
||||
}
|
||||
|
||||
@PutMapping("/{id}")
|
||||
public UserDto updateUser(@RequestBody @Valid UserDto userDto){
|
||||
return new UserDto(userService.updateUser(userDto));
|
||||
}
|
||||
|
||||
|
||||
@DeleteMapping("/{id}")
|
||||
public void deleteUser(@PathVariable Long id) {
|
||||
userService.deleteUser(id);
|
||||
}
|
||||
}
|
@ -0,0 +1,50 @@
|
||||
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 long id;
|
||||
private String login;
|
||||
private UserRole role;
|
||||
private String password;
|
||||
|
||||
public UserDto(){}
|
||||
public UserDto(User user) {
|
||||
this.id = user.getId();
|
||||
this.login = user.getLogin();
|
||||
this.role = user.getRole();
|
||||
this.password = user.getPassword();
|
||||
}
|
||||
|
||||
public long getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public String getLogin() {
|
||||
return login;
|
||||
}
|
||||
|
||||
public UserRole getRole() {
|
||||
return role;
|
||||
}
|
||||
|
||||
public String getPassword()
|
||||
{
|
||||
return password;
|
||||
}
|
||||
|
||||
public void setId(Long id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public void setLogin(String login)
|
||||
{
|
||||
this.login = login;
|
||||
}
|
||||
|
||||
public void setPassword(String password)
|
||||
{
|
||||
this.password = password;
|
||||
}
|
||||
}
|
@ -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,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,91 @@
|
||||
package ru.ulstu.is.sbapp.HardwareShop.models;
|
||||
|
||||
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
import jakarta.validation.constraints.Size;
|
||||
import ru.ulstu.is.sbapp.HardwareShop.controller.UserSignUpDTO;
|
||||
|
||||
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 User(UserSignUpDTO userDto){
|
||||
this.login = userDto.getLogin();
|
||||
this.password = userDto.getPassword();
|
||||
this.role = UserRole.USER;
|
||||
}
|
||||
|
||||
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,158 @@
|
||||
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();
|
||||
List<Product> result = new ArrayList<>();
|
||||
|
||||
for(Product product : filter) {
|
||||
boolean flag = false;
|
||||
for (Manufacturer manufacturer : product.getManufacturerList()) {
|
||||
if (manufacturers.contains(manufacturer)) {
|
||||
flag = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (flag)
|
||||
{
|
||||
result.add(product);
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
@ -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,129 @@
|
||||
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 org.springframework.util.StringUtils;
|
||||
import ru.ulstu.is.sbapp.HardwareShop.controller.UserDto;
|
||||
import ru.ulstu.is.sbapp.HardwareShop.controller.UserSignUpDTO;
|
||||
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 ru.ulstu.is.sbapp.jwt.JwtException;
|
||||
import ru.ulstu.is.sbapp.jwt.JwtProvider;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
|
||||
@Service
|
||||
public class UserService implements UserDetailsService {
|
||||
private final UserRepository userRepository;
|
||||
private final PasswordEncoder passwordEncoder;
|
||||
private final JwtProvider jwtProvider;
|
||||
|
||||
public UserService(UserRepository userRepository,
|
||||
PasswordEncoder passwordEncoder,
|
||||
JwtProvider jwtProvider){
|
||||
this.userRepository = userRepository;
|
||||
this.passwordEncoder = passwordEncoder;
|
||||
this.jwtProvider = jwtProvider;
|
||||
}
|
||||
public User findByLogin(String login) {
|
||||
return userRepository.findOneByLoginIgnoreCase(login);
|
||||
}
|
||||
public Page<User> findAllPages(int page, int size) {
|
||||
return userRepository.findAll(PageRequest.of(page - 1, size, Sort.by("id").ascending()));
|
||||
}
|
||||
@Transactional
|
||||
public User addUser(UserSignUpDTO userDto){
|
||||
final User user = new User(userDto);
|
||||
userRepository.save(user);
|
||||
return user;
|
||||
}
|
||||
@Transactional
|
||||
public User addUser(String login, String password,
|
||||
String passwordConfirm,
|
||||
UserRole role){
|
||||
if (findByLogin(login) != null) {
|
||||
throw new ValidationException(String.format("User '%s' already exists", login));
|
||||
}
|
||||
if (!Objects.equals(password, passwordConfirm)) {
|
||||
throw new ValidationException("Passwords not equals");
|
||||
}
|
||||
final User user = new User(login,passwordEncoder.encode(password),role);
|
||||
return userRepository.save(user);
|
||||
}
|
||||
@Transactional(readOnly = true)
|
||||
public User findUser(Long id) {
|
||||
final Optional<User> user = userRepository.findById(id);
|
||||
return user.orElseThrow(() -> new UserNotFoundException(id));
|
||||
}
|
||||
|
||||
@Transactional(readOnly = true)
|
||||
public List<User> findAllUsers() {
|
||||
return userRepository.findAll();
|
||||
}
|
||||
|
||||
@Transactional
|
||||
public User updateUser(UserDto userDto) {
|
||||
final User currentUser = findUser(userDto.getId());
|
||||
currentUser.setLogin(userDto.getLogin());
|
||||
currentUser.setPassword(userDto.getPassword());
|
||||
return userRepository.save(currentUser);
|
||||
}
|
||||
@Transactional
|
||||
public User updateUser(Long id,String login, String password) {
|
||||
if (!StringUtils.hasText(login) || !StringUtils.hasText(password)) {
|
||||
throw new IllegalArgumentException("User name, login or password is null or empty");
|
||||
}
|
||||
final User currentUser = findUser(id);
|
||||
currentUser.setLogin(login);
|
||||
currentUser.setPassword(password);
|
||||
return userRepository.save(currentUser);
|
||||
}
|
||||
@Transactional
|
||||
public void deleteUser(Long id) {
|
||||
userRepository.deleteById(id);
|
||||
}
|
||||
|
||||
@Transactional
|
||||
public void deleteAllUsers() {
|
||||
userRepository.deleteAll();
|
||||
}
|
||||
|
||||
@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()));
|
||||
}
|
||||
public String loginAndGetToken(UserDto userDto) {
|
||||
final User user = findByLogin(userDto.getLogin());
|
||||
if (user == null) {
|
||||
}
|
||||
if (!passwordEncoder.matches(userDto.getPassword(), user.getPassword())) {
|
||||
}
|
||||
return jwtProvider.generateToken(user.getLogin());
|
||||
}
|
||||
public UserDetails loadUserByToken(String token) throws UsernameNotFoundException {
|
||||
if (!jwtProvider.isTokenValid(token)) {
|
||||
throw new JwtException("Bad token");
|
||||
}
|
||||
final String userLogin = jwtProvider.getLoginFromToken(token)
|
||||
.orElseThrow(() -> new JwtException("Token is not contain Login"));
|
||||
return loadUserByUsername(userLogin);
|
||||
}
|
||||
}
|
@ -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) {
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
83
src/main/java/ru/ulstu/is/sbapp/SecurityConfiguration.java
Normal file
83
src/main/java/ru/ulstu/is/sbapp/SecurityConfiguration.java
Normal file
@ -0,0 +1,83 @@
|
||||
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.config.http.SessionCreationPolicy;
|
||||
import org.springframework.security.web.SecurityFilterChain;
|
||||
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
|
||||
import ru.ulstu.is.sbapp.HardwareShop.controller.UserController;
|
||||
import ru.ulstu.is.sbapp.HardwareShop.models.UserRole;
|
||||
import ru.ulstu.is.sbapp.HardwareShop.services.UserService;
|
||||
import ru.ulstu.is.sbapp.jwt.JwtFilter;
|
||||
|
||||
@Configuration
|
||||
@EnableWebSecurity
|
||||
@EnableMethodSecurity(
|
||||
securedEnabled = true
|
||||
)
|
||||
public class SecurityConfiguration {
|
||||
private final Logger log = LoggerFactory.getLogger(SecurityConfiguration.class);
|
||||
private static final String LOGIN_URL = "/login";
|
||||
public static final String SPA_URL_MASK = "/{path:[^\\.]*}";
|
||||
|
||||
private final UserService userService;
|
||||
private final JwtFilter jwtFilter;
|
||||
|
||||
public SecurityConfiguration(UserService userService)
|
||||
{
|
||||
this.userService = userService;
|
||||
this.jwtFilter = new JwtFilter(userService);
|
||||
createAdminOnStartup();
|
||||
}
|
||||
|
||||
private void createAdminOnStartup() {
|
||||
final String admin = "admin";
|
||||
if (userService.findByLogin(admin) == null) {
|
||||
log.info("Admin user successfully created");
|
||||
userService.addUser(admin, admin, admin, UserRole.ADMIN);
|
||||
}
|
||||
}
|
||||
@Bean
|
||||
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
|
||||
http.cors()
|
||||
.and()
|
||||
.csrf().disable()
|
||||
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
|
||||
.and()
|
||||
.authorizeHttpRequests()
|
||||
.antMatchers("/", SPA_URL_MASK).permitAll()
|
||||
.antMatchers(HttpMethod.POST, UserController.URL_LOGIN).permitAll()
|
||||
.antMatchers(HttpMethod.POST, UserController.URL_SIGN_UP).permitAll()
|
||||
.antMatchers(HttpMethod.POST, UserController.URL_WHO_AM_I).permitAll()
|
||||
.anyRequest()
|
||||
.authenticated()
|
||||
.and()
|
||||
.addFilterBefore(jwtFilter, UsernamePasswordAuthenticationFilter.class)
|
||||
.anonymous();
|
||||
return http.userDetailsService(userService).build();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public WebSecurityCustomizer webSecurityCustomizer() {
|
||||
return (web) -> web.ignoring()
|
||||
.antMatchers(HttpMethod.OPTIONS, "/**")
|
||||
.antMatchers("/*.js")
|
||||
.antMatchers("/*.html")
|
||||
.antMatchers("/*.css")
|
||||
.antMatchers("/assets/**")
|
||||
.antMatchers("/favicon.ico")
|
||||
.antMatchers("/.js", "/.css")
|
||||
.antMatchers("/swagger-ui/index.html")
|
||||
.antMatchers("/webjars/**")
|
||||
.antMatchers("/swagger-resources/**")
|
||||
.antMatchers("/v3/api-docs/**")
|
||||
.antMatchers("/h2-console/**");
|
||||
}
|
||||
}
|
@ -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()));
|
||||
}
|
||||
}
|
||||
}
|
21
src/main/java/ru/ulstu/is/sbapp/WebConfiguration.java
Normal file
21
src/main/java/ru/ulstu/is/sbapp/WebConfiguration.java
Normal file
@ -0,0 +1,21 @@
|
||||
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("product");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addCorsMappings(CorsRegistry registry) {
|
||||
registry.addMapping("/**").allowedMethods("*");
|
||||
}
|
||||
}
|
11
src/main/java/ru/ulstu/is/sbapp/jwt/JwtException.java
Normal file
11
src/main/java/ru/ulstu/is/sbapp/jwt/JwtException.java
Normal file
@ -0,0 +1,11 @@
|
||||
package ru.ulstu.is.sbapp.jwt;
|
||||
|
||||
public class JwtException extends RuntimeException {
|
||||
public JwtException(Throwable throwable) {
|
||||
super(throwable);
|
||||
}
|
||||
|
||||
public JwtException(String message) {
|
||||
super(message);
|
||||
}
|
||||
}
|
72
src/main/java/ru/ulstu/is/sbapp/jwt/JwtFilter.java
Normal file
72
src/main/java/ru/ulstu/is/sbapp/jwt/JwtFilter.java
Normal file
@ -0,0 +1,72 @@
|
||||
package ru.ulstu.is.sbapp.jwt;
|
||||
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import javax.servlet.FilterChain;
|
||||
import javax.servlet.ServletException;
|
||||
import javax.servlet.ServletRequest;
|
||||
import javax.servlet.ServletResponse;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
|
||||
import org.springframework.security.core.context.SecurityContextHolder;
|
||||
import org.springframework.security.core.userdetails.UserDetails;
|
||||
import org.springframework.util.StringUtils;
|
||||
import org.springframework.web.filter.GenericFilterBean;
|
||||
import ru.ulstu.is.sbapp.HardwareShop.services.UserService;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
public class JwtFilter extends GenericFilterBean {
|
||||
private static final String AUTHORIZATION = "Authorization";
|
||||
public static final String TOKEN_BEGIN_STR = "Bearer ";
|
||||
|
||||
private final UserService userService;
|
||||
|
||||
public JwtFilter(UserService userService) {
|
||||
this.userService = userService;
|
||||
}
|
||||
|
||||
private String getTokenFromRequest(HttpServletRequest request) {
|
||||
String bearer = request.getHeader(AUTHORIZATION);
|
||||
if (StringUtils.hasText(bearer) && bearer.startsWith(TOKEN_BEGIN_STR)) {
|
||||
return bearer.substring(TOKEN_BEGIN_STR.length());
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private void raiseException(ServletResponse response, int status, String message) throws IOException {
|
||||
if (response instanceof final HttpServletResponse httpResponse) {
|
||||
httpResponse.setContentType(MediaType.APPLICATION_JSON_VALUE);
|
||||
httpResponse.setStatus(status);
|
||||
final byte[] body = new ObjectMapper().writeValueAsBytes(message);
|
||||
response.getOutputStream().write(body);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void doFilter(ServletRequest request,
|
||||
ServletResponse response,
|
||||
FilterChain chain) throws IOException, ServletException {
|
||||
if (request instanceof final HttpServletRequest httpRequest) {
|
||||
final String token = getTokenFromRequest(httpRequest);
|
||||
if (StringUtils.hasText(token)) {
|
||||
try {
|
||||
final UserDetails user = userService.loadUserByToken(token);
|
||||
final UsernamePasswordAuthenticationToken auth =
|
||||
new UsernamePasswordAuthenticationToken(user, null, user.getAuthorities());
|
||||
SecurityContextHolder.getContext().setAuthentication(auth);
|
||||
} catch (JwtException e) {
|
||||
raiseException(response, HttpServletResponse.SC_UNAUTHORIZED, e.getMessage());
|
||||
return;
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
raiseException(response, HttpServletResponse.SC_INTERNAL_SERVER_ERROR,
|
||||
String.format("Internal error: %s", e.getMessage()));
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
chain.doFilter(request, response);
|
||||
}
|
||||
}
|
27
src/main/java/ru/ulstu/is/sbapp/jwt/JwtProperties.java
Normal file
27
src/main/java/ru/ulstu/is/sbapp/jwt/JwtProperties.java
Normal file
@ -0,0 +1,27 @@
|
||||
package ru.ulstu.is.sbapp.jwt;
|
||||
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
@Configuration
|
||||
@ConfigurationProperties(prefix = "jwt", ignoreInvalidFields = true)
|
||||
public class JwtProperties {
|
||||
private String devToken = "";
|
||||
private Boolean isDev = true;
|
||||
|
||||
public String getDevToken() {
|
||||
return devToken;
|
||||
}
|
||||
|
||||
public void setDevToken(String devToken) {
|
||||
this.devToken = devToken;
|
||||
}
|
||||
|
||||
public Boolean isDev() {
|
||||
return isDev;
|
||||
}
|
||||
|
||||
public void setDev(Boolean dev) {
|
||||
isDev = dev;
|
||||
}
|
||||
}
|
108
src/main/java/ru/ulstu/is/sbapp/jwt/JwtProvider.java
Normal file
108
src/main/java/ru/ulstu/is/sbapp/jwt/JwtProvider.java
Normal file
@ -0,0 +1,108 @@
|
||||
package ru.ulstu.is.sbapp.jwt;
|
||||
|
||||
import com.auth0.jwt.JWT;
|
||||
import com.auth0.jwt.algorithms.Algorithm;
|
||||
import com.auth0.jwt.exceptions.JWTVerificationException;
|
||||
import com.auth0.jwt.interfaces.DecodedJWT;
|
||||
import com.auth0.jwt.interfaces.JWTVerifier;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.security.MessageDigest;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.time.LocalDate;
|
||||
import java.time.ZoneId;
|
||||
import java.util.Date;
|
||||
import java.util.Optional;
|
||||
import java.util.UUID;
|
||||
|
||||
@Component
|
||||
public class JwtProvider {
|
||||
private final static Logger LOG = LoggerFactory.getLogger(JwtProvider.class);
|
||||
|
||||
private final static byte[] HEX_ARRAY = "0123456789ABCDEF".getBytes(StandardCharsets.US_ASCII);
|
||||
private final static String ISSUER = "auth0";
|
||||
|
||||
private final Algorithm algorithm;
|
||||
private final JWTVerifier verifier;
|
||||
|
||||
public JwtProvider(JwtProperties jwtProperties) {
|
||||
if (!jwtProperties.isDev()) {
|
||||
LOG.info("Generate new JWT key for prod");
|
||||
try {
|
||||
final MessageDigest salt = MessageDigest.getInstance("SHA-256");
|
||||
salt.update(UUID.randomUUID().toString().getBytes(StandardCharsets.UTF_8));
|
||||
LOG.info("Use generated JWT key for prod \n{}", bytesToHex(salt.digest()));
|
||||
algorithm = Algorithm.HMAC256(bytesToHex(salt.digest()));
|
||||
} catch (NoSuchAlgorithmException e) {
|
||||
throw new JwtException(e);
|
||||
}
|
||||
} else {
|
||||
LOG.info("Use default JWT key for dev \n{}", jwtProperties.getDevToken());
|
||||
algorithm = Algorithm.HMAC256(jwtProperties.getDevToken());
|
||||
}
|
||||
verifier = JWT.require(algorithm)
|
||||
.withIssuer(ISSUER)
|
||||
.build();
|
||||
}
|
||||
|
||||
private static String bytesToHex(byte[] bytes) {
|
||||
byte[] hexChars = new byte[bytes.length * 2];
|
||||
for (int j = 0; j < bytes.length; j++) {
|
||||
int v = bytes[j] & 0xFF;
|
||||
hexChars[j * 2] = HEX_ARRAY[v >>> 4];
|
||||
hexChars[j * 2 + 1] = HEX_ARRAY[v & 0x0F];
|
||||
}
|
||||
return new String(hexChars, StandardCharsets.UTF_8);
|
||||
}
|
||||
|
||||
public String generateToken(String login) {
|
||||
final Date issueDate = Date.from(LocalDate.now()
|
||||
.atStartOfDay(ZoneId.systemDefault())
|
||||
.toInstant());
|
||||
final Date expireDate = Date.from(LocalDate.now()
|
||||
.plusDays(15)
|
||||
.atStartOfDay(ZoneId.systemDefault())
|
||||
.toInstant());
|
||||
var temp = JWT.create();
|
||||
var temp2 = temp.withIssuer(ISSUER);
|
||||
var temp3 = temp2.withIssuedAt(issueDate);
|
||||
var temp4 = temp3.withExpiresAt(expireDate);
|
||||
var temp5 = temp4.withSubject(login);
|
||||
var temp6 = temp5.sign(algorithm);
|
||||
return temp6;
|
||||
}
|
||||
|
||||
private DecodedJWT validateToken(String token) {
|
||||
try {
|
||||
return verifier.verify(token);
|
||||
} catch (JWTVerificationException e) {
|
||||
throw new JwtException(String.format("Token verification error: %s", e.getMessage()));
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isTokenValid(String token) {
|
||||
if (!StringUtils.hasText(token)) {
|
||||
return false;
|
||||
}
|
||||
try {
|
||||
validateToken(token);
|
||||
return true;
|
||||
} catch (JwtException e) {
|
||||
LOG.error(e.getMessage());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public Optional<String> getLoginFromToken(String token) {
|
||||
try {
|
||||
return Optional.ofNullable(validateToken(token).getSubject());
|
||||
} catch (JwtException e) {
|
||||
LOG.error(e.getMessage());
|
||||
return Optional.empty();
|
||||
}
|
||||
}
|
||||
}
|
@ -1 +1,14 @@
|
||||
|
||||
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
|
||||
jwt.dev=true
|
||||
jwt.secret = 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>
|
48
src/main/resources/templates/default.html
Normal file
48
src/main/resources/templates/default.html
Normal file
@ -0,0 +1,48 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en"
|
||||
xmlns:th="http://www.thymeleaf.org"
|
||||
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>
|
||||
</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>
|
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}" enctype="manufacturerid/form-data" method="get">
|
||||
<div class="input-group mb-3">
|
||||
<select name="selected" class="form-select" multiple="multiple">
|
||||
<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>
|
67
src/main/resources/templates/products.html
Normal file
67
src/main/resources/templates/products.html
Normal file
@ -0,0 +1,67 @@
|
||||
<!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>
|
||||
<a class="btn btn-success button-fixed"
|
||||
th:href="@{/product/}">
|
||||
<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>
|
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,91 @@
|
||||
package ru.ulstu.is.sbapp;
|
||||
|
||||
import org.junit.jupiter.api.Assertions;
|
||||
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 ru.ulstu.is.sbapp.calc.service.CalculatorService;
|
||||
|
||||
@SpringBootTest
|
||||
class SbappApplicationTests {
|
||||
@Autowired
|
||||
CalculatorService calculatorService;
|
||||
|
||||
@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