Compare commits
44 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
233d338cb0 | ||
|
8f8d048296 | ||
|
ab784c2d69 | ||
|
f11901be1f | ||
|
0fc185659c | ||
|
b4e93f7719 | ||
|
b08c3f8162 | ||
|
e2d54386c9 | ||
|
4c81ef4515 | ||
|
845d28ad0a | ||
|
dbff5ad51e | ||
|
b27e0717e5 | ||
|
57875bddcd | ||
|
855813a4d5 | ||
|
b75177292e | ||
|
abc103ea52 | ||
|
6399f34e54 | ||
|
4d1ef68a0a | ||
|
1a8c133033 | ||
|
c36a2253f9 | ||
|
90138ca93d | ||
|
e8a77a66d1 | ||
|
ca5b3d06f1 | ||
|
d8b298fc5f | ||
|
f12e9dfad5 | ||
|
16114c71a5 | ||
|
7a7cabf3e5 | ||
|
71e4d822c7 | ||
|
458a880c44 | ||
|
82903666e5 | ||
|
c547136cbf | ||
|
c26da9cafc | ||
|
7094911275 | ||
|
6e03778568 | ||
|
6be1a9986c | ||
|
5fcf8ab796 | ||
|
f448d6541a | ||
|
f388f69e51 | ||
|
9d53595074 | ||
|
c4eb1070e1 | ||
|
abd50a63ed | ||
|
7ba6f53b0e | ||
|
3239674edf | ||
|
93bdee37f3 |
45
.gitignore
vendored
45
.gitignore
vendored
@ -1,14 +1,37 @@
|
||||
# ---> VisualStudioCode
|
||||
.vscode/*
|
||||
!.vscode/settings.json
|
||||
!.vscode/tasks.json
|
||||
!.vscode/launch.json
|
||||
!.vscode/extensions.json
|
||||
!.vscode/*.code-snippets
|
||||
HELP.md
|
||||
.gradle
|
||||
build/
|
||||
!gradle/wrapper/gradle-wrapper.jar
|
||||
!**/src/main/**/build/
|
||||
!**/src/test/**/build/
|
||||
|
||||
# Local History for Visual Studio Code
|
||||
.history/
|
||||
### STS ###
|
||||
.apt_generated
|
||||
.classpath
|
||||
.factorypath
|
||||
.project
|
||||
.settings
|
||||
.springBeans
|
||||
.sts4-cache
|
||||
bin/
|
||||
!**/src/main/**/bin/
|
||||
!**/src/test/**/bin/
|
||||
|
||||
# Built Visual Studio Code Extensions
|
||||
*.vsix
|
||||
### IntelliJ IDEA ###
|
||||
.idea
|
||||
*.iws
|
||||
*.iml
|
||||
*.ipr
|
||||
out/
|
||||
!**/src/main/**/out/
|
||||
!**/src/test/**/out/
|
||||
|
||||
### NetBeans ###
|
||||
/nbproject/private/
|
||||
/nbbuild/
|
||||
/dist/
|
||||
/nbdist/
|
||||
/.nb-gradle/
|
||||
|
||||
### VS Code ###
|
||||
.vscode/
|
||||
|
18
README.md
18
README.md
@ -1,2 +1,20 @@
|
||||
# Internet_Programming_2
|
||||
|
||||
Swagger UI: \
|
||||
http://localhost:8080/swagger-ui/index.html
|
||||
|
||||
H2 Console: \
|
||||
http://localhost:8080/h2-console
|
||||
|
||||
JDBC URL: jdbc:h2:file:./data \
|
||||
User Name: sa \
|
||||
Password: password
|
||||
|
||||
Почитать:
|
||||
|
||||
- Односторонние и двусторонние связи https://www.baeldung.com/jpa-hibernate-associations
|
||||
- Getters и Setters для двусторонних связей https://en.wikibooks.org/wiki/Java_Persistence/OneToMany#Getters_and_Setters
|
||||
- Многие-ко-многим с доп. атрибутами https://thorben-janssen.com/hibernate-tip-many-to-many-association-with-additional-attributes/
|
||||
- Многие-ко-многим с доп. атрибутами https://www.baeldung.com/jpa-many-to-many
|
||||
- Каскадное удаление сущностей со связями многие-ко-многим https://www.baeldung.com/jpa-remove-entity-many-to-many
|
||||
- Выбор типа коллекции для отношений вида ко-многим в JPA https://thorben-janssen.com/association-mappings-bag-list-set/
|
||||
|
53
build.gradle
Normal file
53
build.gradle
Normal file
@ -0,0 +1,53 @@
|
||||
plugins {
|
||||
id 'java'
|
||||
id 'org.springframework.boot' version '3.2.4'
|
||||
id 'io.spring.dependency-management' version '1.1.4'
|
||||
}
|
||||
|
||||
group = 'com.example'
|
||||
version = '0.0.1-SNAPSHOT'
|
||||
|
||||
defaultTasks 'bootRun'
|
||||
|
||||
jar {
|
||||
enabled = false
|
||||
}
|
||||
|
||||
bootJar {
|
||||
archiveFileName = String.format('%s-%s.jar', rootProject.name, version)
|
||||
}
|
||||
|
||||
assert System.properties['java.specification.version'] == '17' || '21'
|
||||
|
||||
java {
|
||||
sourceCompatibility = '17'
|
||||
}
|
||||
|
||||
repositories {
|
||||
mavenCentral()
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation 'org.springframework.boot:spring-boot-starter-web'
|
||||
implementation 'org.springframework.boot:spring-boot-starter-validation'
|
||||
implementation 'org.modelmapper:modelmapper:3.2.0'
|
||||
|
||||
developmentOnly 'org.springframework.boot:spring-boot-devtools'
|
||||
implementation 'org.springframework.boot:spring-boot-starter-thymeleaf'
|
||||
implementation 'nz.net.ultraq.thymeleaf:thymeleaf-layout-dialect:3.3.0'
|
||||
implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
|
||||
implementation 'com.h2database:h2:2.2.224'
|
||||
|
||||
runtimeOnly 'org.webjars.npm:bootstrap:5.3.3'
|
||||
runtimeOnly 'org.webjars.npm:bootstrap-icons:1.11.3'
|
||||
runtimeOnly 'org.webjars.npm:font-awesome:4.7.0'
|
||||
|
||||
implementation 'org.springframework.boot:spring-boot-starter-security'
|
||||
implementation 'org.thymeleaf.extras:thymeleaf-extras-springsecurity6'
|
||||
|
||||
testImplementation 'org.springframework.boot:spring-boot-starter-test'
|
||||
}
|
||||
|
||||
tasks.named('test') {
|
||||
useJUnitPlatform()
|
||||
}
|
BIN
data.mv.db
Normal file
BIN
data.mv.db
Normal file
Binary file not shown.
38659
data.trace.db
Normal file
38659
data.trace.db
Normal file
File diff suppressed because it is too large
Load Diff
BIN
gradle/wrapper/gradle-wrapper.jar
vendored
Normal file
BIN
gradle/wrapper/gradle-wrapper.jar
vendored
Normal file
Binary file not shown.
10
gradle/wrapper/gradle-wrapper.properties
vendored
Normal file
10
gradle/wrapper/gradle-wrapper.properties
vendored
Normal file
@ -0,0 +1,10 @@
|
||||
distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-8.5-bin.zip
|
||||
networkTimeout=10000
|
||||
validateDistributionUrl=true
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
zipStorePath=wrapper/dists
|
||||
org.gradle.daemon=true
|
||||
org.gradle.parallel=true
|
||||
org.gradle.configureondemand=true
|
249
gradlew
vendored
Normal file
249
gradlew
vendored
Normal file
@ -0,0 +1,249 @@
|
||||
#!/bin/sh
|
||||
|
||||
#
|
||||
# Copyright © 2015-2021 the original authors.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# https://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
#
|
||||
|
||||
##############################################################################
|
||||
#
|
||||
# Gradle start up script for POSIX generated by Gradle.
|
||||
#
|
||||
# Important for running:
|
||||
#
|
||||
# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is
|
||||
# noncompliant, but you have some other compliant shell such as ksh or
|
||||
# bash, then to run this script, type that shell name before the whole
|
||||
# command line, like:
|
||||
#
|
||||
# ksh Gradle
|
||||
#
|
||||
# Busybox and similar reduced shells will NOT work, because this script
|
||||
# requires all of these POSIX shell features:
|
||||
# * functions;
|
||||
# * expansions «$var», «${var}», «${var:-default}», «${var+SET}»,
|
||||
# «${var#prefix}», «${var%suffix}», and «$( cmd )»;
|
||||
# * compound commands having a testable exit status, especially «case»;
|
||||
# * various built-in commands including «command», «set», and «ulimit».
|
||||
#
|
||||
# Important for patching:
|
||||
#
|
||||
# (2) This script targets any POSIX shell, so it avoids extensions provided
|
||||
# by Bash, Ksh, etc; in particular arrays are avoided.
|
||||
#
|
||||
# The "traditional" practice of packing multiple parameters into a
|
||||
# space-separated string is a well documented source of bugs and security
|
||||
# problems, so this is (mostly) avoided, by progressively accumulating
|
||||
# options in "$@", and eventually passing that to Java.
|
||||
#
|
||||
# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS,
|
||||
# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly;
|
||||
# see the in-line comments for details.
|
||||
#
|
||||
# There are tweaks for specific operating systems such as AIX, CygWin,
|
||||
# Darwin, MinGW, and NonStop.
|
||||
#
|
||||
# (3) This script is generated from the Groovy template
|
||||
# https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
|
||||
# within the Gradle project.
|
||||
#
|
||||
# You can find Gradle at https://github.com/gradle/gradle/.
|
||||
#
|
||||
##############################################################################
|
||||
|
||||
# Attempt to set APP_HOME
|
||||
|
||||
# Resolve links: $0 may be a link
|
||||
app_path=$0
|
||||
|
||||
# Need this for daisy-chained symlinks.
|
||||
while
|
||||
APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path
|
||||
[ -h "$app_path" ]
|
||||
do
|
||||
ls=$( ls -ld "$app_path" )
|
||||
link=${ls#*' -> '}
|
||||
case $link in #(
|
||||
/*) app_path=$link ;; #(
|
||||
*) app_path=$APP_HOME$link ;;
|
||||
esac
|
||||
done
|
||||
|
||||
# This is normally unused
|
||||
# shellcheck disable=SC2034
|
||||
APP_BASE_NAME=${0##*/}
|
||||
# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036)
|
||||
APP_HOME=$( cd "${APP_HOME:-./}" > /dev/null && pwd -P ) || exit
|
||||
|
||||
# Use the maximum available, or set MAX_FD != -1 to use that value.
|
||||
MAX_FD=maximum
|
||||
|
||||
warn () {
|
||||
echo "$*"
|
||||
} >&2
|
||||
|
||||
die () {
|
||||
echo
|
||||
echo "$*"
|
||||
echo
|
||||
exit 1
|
||||
} >&2
|
||||
|
||||
# OS specific support (must be 'true' or 'false').
|
||||
cygwin=false
|
||||
msys=false
|
||||
darwin=false
|
||||
nonstop=false
|
||||
case "$( uname )" in #(
|
||||
CYGWIN* ) cygwin=true ;; #(
|
||||
Darwin* ) darwin=true ;; #(
|
||||
MSYS* | MINGW* ) msys=true ;; #(
|
||||
NONSTOP* ) nonstop=true ;;
|
||||
esac
|
||||
|
||||
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
|
||||
|
||||
|
||||
# Determine the Java command to use to start the JVM.
|
||||
if [ -n "$JAVA_HOME" ] ; then
|
||||
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
|
||||
# IBM's JDK on AIX uses strange locations for the executables
|
||||
JAVACMD=$JAVA_HOME/jre/sh/java
|
||||
else
|
||||
JAVACMD=$JAVA_HOME/bin/java
|
||||
fi
|
||||
if [ ! -x "$JAVACMD" ] ; then
|
||||
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
|
||||
|
||||
Please set the JAVA_HOME variable in your environment to match the
|
||||
location of your Java installation."
|
||||
fi
|
||||
else
|
||||
JAVACMD=java
|
||||
if ! command -v java >/dev/null 2>&1
|
||||
then
|
||||
die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
||||
|
||||
Please set the JAVA_HOME variable in your environment to match the
|
||||
location of your Java installation."
|
||||
fi
|
||||
fi
|
||||
|
||||
# Increase the maximum file descriptors if we can.
|
||||
if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
|
||||
case $MAX_FD in #(
|
||||
max*)
|
||||
# In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked.
|
||||
# shellcheck disable=SC2039,SC3045
|
||||
MAX_FD=$( ulimit -H -n ) ||
|
||||
warn "Could not query maximum file descriptor limit"
|
||||
esac
|
||||
case $MAX_FD in #(
|
||||
'' | soft) :;; #(
|
||||
*)
|
||||
# In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked.
|
||||
# shellcheck disable=SC2039,SC3045
|
||||
ulimit -n "$MAX_FD" ||
|
||||
warn "Could not set maximum file descriptor limit to $MAX_FD"
|
||||
esac
|
||||
fi
|
||||
|
||||
# Collect all arguments for the java command, stacking in reverse order:
|
||||
# * args from the command line
|
||||
# * the main class name
|
||||
# * -classpath
|
||||
# * -D...appname settings
|
||||
# * --module-path (only if needed)
|
||||
# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables.
|
||||
|
||||
# For Cygwin or MSYS, switch paths to Windows format before running java
|
||||
if "$cygwin" || "$msys" ; then
|
||||
APP_HOME=$( cygpath --path --mixed "$APP_HOME" )
|
||||
CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" )
|
||||
|
||||
JAVACMD=$( cygpath --unix "$JAVACMD" )
|
||||
|
||||
# Now convert the arguments - kludge to limit ourselves to /bin/sh
|
||||
for arg do
|
||||
if
|
||||
case $arg in #(
|
||||
-*) false ;; # don't mess with options #(
|
||||
/?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath
|
||||
[ -e "$t" ] ;; #(
|
||||
*) false ;;
|
||||
esac
|
||||
then
|
||||
arg=$( cygpath --path --ignore --mixed "$arg" )
|
||||
fi
|
||||
# Roll the args list around exactly as many times as the number of
|
||||
# args, so each arg winds up back in the position where it started, but
|
||||
# possibly modified.
|
||||
#
|
||||
# NB: a `for` loop captures its iteration list before it begins, so
|
||||
# changing the positional parameters here affects neither the number of
|
||||
# iterations, nor the values presented in `arg`.
|
||||
shift # remove old arg
|
||||
set -- "$@" "$arg" # push replacement arg
|
||||
done
|
||||
fi
|
||||
|
||||
|
||||
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
|
||||
|
||||
# Collect all arguments for the java command:
|
||||
# * DEFAULT_JVM_OPTS, JAVA_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments,
|
||||
# and any embedded shellness will be escaped.
|
||||
# * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be
|
||||
# treated as '${Hostname}' itself on the command line.
|
||||
|
||||
set -- \
|
||||
"-Dorg.gradle.appname=$APP_BASE_NAME" \
|
||||
-classpath "$CLASSPATH" \
|
||||
org.gradle.wrapper.GradleWrapperMain \
|
||||
"$@"
|
||||
|
||||
# Stop when "xargs" is not available.
|
||||
if ! command -v xargs >/dev/null 2>&1
|
||||
then
|
||||
die "xargs is not available"
|
||||
fi
|
||||
|
||||
# Use "xargs" to parse quoted args.
|
||||
#
|
||||
# With -n1 it outputs one arg per line, with the quotes and backslashes removed.
|
||||
#
|
||||
# In Bash we could simply go:
|
||||
#
|
||||
# readarray ARGS < <( xargs -n1 <<<"$var" ) &&
|
||||
# set -- "${ARGS[@]}" "$@"
|
||||
#
|
||||
# but POSIX shell has neither arrays nor command substitution, so instead we
|
||||
# post-process each arg (as a line of input to sed) to backslash-escape any
|
||||
# character that might be a shell metacharacter, then use eval to reverse
|
||||
# that process (while maintaining the separation between arguments), and wrap
|
||||
# the whole thing up as a single "set" statement.
|
||||
#
|
||||
# This will of course break if any of these variables contains a newline or
|
||||
# an unmatched quote.
|
||||
#
|
||||
|
||||
eval "set -- $(
|
||||
printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" |
|
||||
xargs -n1 |
|
||||
sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' |
|
||||
tr '\n' ' '
|
||||
)" '"$@"'
|
||||
|
||||
exec "$JAVACMD" "$@"
|
92
gradlew.bat
vendored
Normal file
92
gradlew.bat
vendored
Normal file
@ -0,0 +1,92 @@
|
||||
@rem
|
||||
@rem Copyright 2015 the original author or authors.
|
||||
@rem
|
||||
@rem Licensed under the Apache License, Version 2.0 (the "License");
|
||||
@rem you may not use this file except in compliance with the License.
|
||||
@rem You may obtain a copy of the License at
|
||||
@rem
|
||||
@rem https://www.apache.org/licenses/LICENSE-2.0
|
||||
@rem
|
||||
@rem Unless required by applicable law or agreed to in writing, software
|
||||
@rem distributed under the License is distributed on an "AS IS" BASIS,
|
||||
@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
@rem See the License for the specific language governing permissions and
|
||||
@rem limitations under the License.
|
||||
@rem
|
||||
|
||||
@if "%DEBUG%"=="" @echo off
|
||||
@rem ##########################################################################
|
||||
@rem
|
||||
@rem Gradle startup script for Windows
|
||||
@rem
|
||||
@rem ##########################################################################
|
||||
|
||||
@rem Set local scope for the variables with windows NT shell
|
||||
if "%OS%"=="Windows_NT" setlocal
|
||||
|
||||
set DIRNAME=%~dp0
|
||||
if "%DIRNAME%"=="" set DIRNAME=.
|
||||
@rem This is normally unused
|
||||
set APP_BASE_NAME=%~n0
|
||||
set APP_HOME=%DIRNAME%
|
||||
|
||||
@rem Resolve any "." and ".." in APP_HOME to make it shorter.
|
||||
for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
|
||||
|
||||
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||
set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
|
||||
|
||||
@rem Find java.exe
|
||||
if defined JAVA_HOME goto findJavaFromJavaHome
|
||||
|
||||
set JAVA_EXE=java.exe
|
||||
%JAVA_EXE% -version >NUL 2>&1
|
||||
if %ERRORLEVEL% equ 0 goto execute
|
||||
|
||||
echo.
|
||||
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
||||
echo.
|
||||
echo Please set the JAVA_HOME variable in your environment to match the
|
||||
echo location of your Java installation.
|
||||
|
||||
goto fail
|
||||
|
||||
:findJavaFromJavaHome
|
||||
set JAVA_HOME=%JAVA_HOME:"=%
|
||||
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
|
||||
|
||||
if exist "%JAVA_EXE%" goto execute
|
||||
|
||||
echo.
|
||||
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
|
||||
echo.
|
||||
echo Please set the JAVA_HOME variable in your environment to match the
|
||||
echo location of your Java installation.
|
||||
|
||||
goto fail
|
||||
|
||||
:execute
|
||||
@rem Setup the command line
|
||||
|
||||
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
|
||||
|
||||
|
||||
@rem Execute Gradle
|
||||
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
|
||||
|
||||
:end
|
||||
@rem End local scope for the variables with windows NT shell
|
||||
if %ERRORLEVEL% equ 0 goto mainEnd
|
||||
|
||||
:fail
|
||||
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
|
||||
rem the _cmd.exe /c_ return code!
|
||||
set EXIT_CODE=%ERRORLEVEL%
|
||||
if %EXIT_CODE% equ 0 set EXIT_CODE=1
|
||||
if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE%
|
||||
exit /b %EXIT_CODE%
|
||||
|
||||
:mainEnd
|
||||
if "%OS%"=="Windows_NT" endlocal
|
||||
|
||||
:omega
|
1
settings.gradle
Normal file
1
settings.gradle
Normal file
@ -0,0 +1 @@
|
||||
rootProject.name = 'demo'
|
66
src/main/java/com/example/demo/DemoApplication.java
Normal file
66
src/main/java/com/example/demo/DemoApplication.java
Normal file
@ -0,0 +1,66 @@
|
||||
package com.example.demo;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.boot.CommandLineRunner;
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
|
||||
import com.example.demo.department.model.DepartmentEntity;
|
||||
import com.example.demo.department.service.DepartmentService;
|
||||
import com.example.demo.directions.model.DirectionsEntity;
|
||||
import com.example.demo.directions.service.DirectionsService;
|
||||
import com.example.demo.entrysData.model.EntrysDataEntity;
|
||||
import com.example.demo.entrysData.service.EntrysDataService;
|
||||
import com.example.demo.news.model.NewsEntity;
|
||||
import com.example.demo.news.service.NewsService;
|
||||
|
||||
@SpringBootApplication
|
||||
public class DemoApplication implements CommandLineRunner {
|
||||
private final Logger log = LoggerFactory.getLogger(DemoApplication.class);
|
||||
|
||||
private final EntrysDataService entrysDataService;
|
||||
private final DepartmentService departmentService;
|
||||
private final NewsService newsService;
|
||||
private final DirectionsService directionsService;
|
||||
|
||||
public DemoApplication(EntrysDataService entrysDataService, DepartmentService departmentService,
|
||||
NewsService newsService, DirectionsService directionsService) {
|
||||
this.entrysDataService = entrysDataService;
|
||||
this.departmentService = departmentService;
|
||||
this.directionsService = directionsService;
|
||||
this.newsService = newsService;
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
SpringApplication.run(DemoApplication.class, args);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run(String... args) throws Exception {
|
||||
if (args.length > 0 && Objects.equals("--populate", args[0])) {
|
||||
log.info("Create departments");
|
||||
final var dep1 = departmentService.create(new DepartmentEntity("ГУМ"));
|
||||
final var dep2 = departmentService.create(new DepartmentEntity("ИСТ"));
|
||||
final var dep3 = departmentService.create(new DepartmentEntity("ИЯ"));
|
||||
|
||||
log.info("Create entrysData");
|
||||
entrysDataService.create(new EntrysDataEntity("login", "password", dep2));
|
||||
entrysDataService.create(new EntrysDataEntity("login1", "password", dep3));
|
||||
entrysDataService.create(new EntrysDataEntity("login2", "password", dep1));
|
||||
|
||||
log.info("Create directions");
|
||||
|
||||
directionsService.create(new DirectionsEntity("09.02.23", "Программная инженерия", dep1, "Информатика"));
|
||||
directionsService.create(new DirectionsEntity("09.03.24", "ИВТ", dep2, "Информатика, математика"));
|
||||
directionsService.create(new DirectionsEntity("02.05.20", "ИСТ", dep3, "Информатика, иностранный языкы"));
|
||||
|
||||
log.info("Create news");
|
||||
newsService.create(new NewsEntity("2024-04-15", "new1", "description1", dep2));
|
||||
newsService.create(new NewsEntity("2024-02-23", "new2", "description2", dep3));
|
||||
newsService.create(new NewsEntity("2024-03-17", "new3", "description3", dep1));
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,18 @@
|
||||
package com.example.demo.core.api;
|
||||
|
||||
import org.springframework.web.bind.annotation.ControllerAdvice;
|
||||
import org.springframework.web.bind.annotation.ModelAttribute;
|
||||
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
|
||||
@ControllerAdvice
|
||||
public class GlobalController {
|
||||
|
||||
public GlobalController() {
|
||||
}
|
||||
|
||||
@ModelAttribute("servletPath")
|
||||
String getRequestServletPath(HttpServletRequest request) {
|
||||
return request.getServletPath();
|
||||
}
|
||||
}
|
@ -0,0 +1,18 @@
|
||||
package com.example.demo.core.api;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.function.Function;
|
||||
|
||||
import org.springframework.data.domain.Page;
|
||||
|
||||
public class PageAttributesMapper {
|
||||
private PageAttributesMapper() {
|
||||
}
|
||||
|
||||
public static <E, D> Map<String, Object> toAttributes(Page<E> page, Function<E, D> mapper) {
|
||||
return Map.of(
|
||||
"items", page.getContent().stream().map(mapper::apply).toList(),
|
||||
"currentPage", page.getNumber(),
|
||||
"totalPages", page.getTotalPages());
|
||||
}
|
||||
}
|
@ -0,0 +1,23 @@
|
||||
package com.example.demo.core.configuration;
|
||||
|
||||
public class Constants {
|
||||
public static final String SEQUENCE_NAME = "hibernate_sequence";
|
||||
|
||||
public static final String API_URL = "/api/1.0";
|
||||
|
||||
public static final int DEFAULT_PAGE_SIZE = 5;
|
||||
|
||||
public static final int DEFAULT_PAGE_SIZE_FOR_NEWS = 4;
|
||||
|
||||
public static final String REDIRECT_VIEW = "redirect:";
|
||||
|
||||
public static final String ADMIN_PREFIX = "/admin";
|
||||
|
||||
public static final String LOGIN_URL = "/login";
|
||||
public static final String LOGOUT_URL = "/logout";
|
||||
|
||||
public static final String DEFAULT_PASSWORD = "123456";
|
||||
|
||||
private Constants() {
|
||||
}
|
||||
}
|
@ -0,0 +1,23 @@
|
||||
package com.example.demo.core.configuration;
|
||||
|
||||
import org.modelmapper.ModelMapper;
|
||||
import org.modelmapper.PropertyMap;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
import com.example.demo.core.model.BaseEntity;
|
||||
|
||||
@Configuration
|
||||
public class MapperConfiguration {
|
||||
@Bean
|
||||
ModelMapper modelMapper() {
|
||||
final ModelMapper mapper = new ModelMapper();
|
||||
mapper.addMappings(new PropertyMap<Object, BaseEntity>() {
|
||||
@Override
|
||||
protected void configure() {
|
||||
skip(destination.getId());
|
||||
}
|
||||
});
|
||||
return mapper;
|
||||
}
|
||||
}
|
@ -0,0 +1,13 @@
|
||||
package com.example.demo.core.configuration;
|
||||
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.web.servlet.config.annotation.ViewControllerRegistry;
|
||||
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
|
||||
|
||||
@Configuration
|
||||
public class WebConfiguration implements WebMvcConfigurer {
|
||||
@Override
|
||||
public void addViewControllers(ViewControllerRegistry registry) {
|
||||
registry.addViewController("/login").setViewName("login");
|
||||
}
|
||||
}
|
@ -0,0 +1,53 @@
|
||||
package com.example.demo.core.error;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.core.annotation.AnnotationUtils;
|
||||
import org.springframework.web.bind.annotation.ControllerAdvice;
|
||||
import org.springframework.web.bind.annotation.ExceptionHandler;
|
||||
import org.springframework.web.bind.annotation.ResponseStatus;
|
||||
import org.springframework.web.servlet.ModelAndView;
|
||||
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
|
||||
@ControllerAdvice
|
||||
public class AdviceController {
|
||||
private final Logger log = LoggerFactory.getLogger(AdviceController.class);
|
||||
|
||||
private static Throwable getRootCause(Throwable throwable) {
|
||||
Throwable rootCause = throwable;
|
||||
while (rootCause.getCause() != null && rootCause.getCause() != rootCause) {
|
||||
rootCause = rootCause.getCause();
|
||||
}
|
||||
return rootCause;
|
||||
}
|
||||
|
||||
private static Map<String, Object> getAttributes(HttpServletRequest request, Throwable throwable) {
|
||||
final Throwable rootCause = getRootCause(throwable);
|
||||
final StackTraceElement firstError = rootCause.getStackTrace()[0];
|
||||
return Map.of(
|
||||
"message", rootCause.getMessage(),
|
||||
"url", request.getRequestURL(),
|
||||
"exception", rootCause.getClass().getName(),
|
||||
"file", firstError.getFileName(),
|
||||
"method", firstError.getMethodName(),
|
||||
"line", firstError.getLineNumber());
|
||||
}
|
||||
|
||||
@ExceptionHandler(value = Exception.class)
|
||||
public ModelAndView defaultErrorHandler(HttpServletRequest request, Throwable throwable) throws Throwable {
|
||||
if (AnnotationUtils.findAnnotation(throwable.getClass(),
|
||||
ResponseStatus.class) != null) {
|
||||
throw throwable;
|
||||
}
|
||||
|
||||
log.error("{}", throwable.getMessage());
|
||||
throwable.printStackTrace();
|
||||
final ModelAndView model = new ModelAndView();
|
||||
model.addAllObjects(getAttributes(request, throwable));
|
||||
model.setViewName("error");
|
||||
return model;
|
||||
}
|
||||
}
|
@ -0,0 +1,7 @@
|
||||
package com.example.demo.core.error;
|
||||
|
||||
public class NotFoundException extends RuntimeException {
|
||||
public <T> NotFoundException(Class<T> clazz, Long id) {
|
||||
super(String.format("%s with id [%s] is not found or not exists", clazz.getSimpleName(), id));
|
||||
}
|
||||
}
|
28
src/main/java/com/example/demo/core/model/BaseEntity.java
Normal file
28
src/main/java/com/example/demo/core/model/BaseEntity.java
Normal file
@ -0,0 +1,28 @@
|
||||
package com.example.demo.core.model;
|
||||
|
||||
import com.example.demo.core.configuration.Constants;
|
||||
|
||||
import jakarta.persistence.GeneratedValue;
|
||||
import jakarta.persistence.GenerationType;
|
||||
import jakarta.persistence.Id;
|
||||
import jakarta.persistence.MappedSuperclass;
|
||||
import jakarta.persistence.SequenceGenerator;
|
||||
|
||||
@MappedSuperclass
|
||||
public abstract class BaseEntity {
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.SEQUENCE, generator = Constants.SEQUENCE_NAME)
|
||||
@SequenceGenerator(name = Constants.SEQUENCE_NAME, sequenceName = Constants.SEQUENCE_NAME, allocationSize = 1)
|
||||
protected Long id;
|
||||
|
||||
protected BaseEntity() {
|
||||
}
|
||||
|
||||
public Long getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(Long id) {
|
||||
this.id = id;
|
||||
}
|
||||
}
|
@ -0,0 +1,64 @@
|
||||
package com.example.demo.core.security;
|
||||
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.security.authentication.dao.DaoAuthenticationProvider;
|
||||
import org.springframework.security.config.Customizer;
|
||||
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
|
||||
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
|
||||
import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer;
|
||||
import org.springframework.security.config.annotation.web.configurers.HeadersConfigurer.FrameOptionsConfig;
|
||||
import org.springframework.security.core.userdetails.UserDetailsService;
|
||||
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
|
||||
import org.springframework.security.crypto.password.PasswordEncoder;
|
||||
import org.springframework.security.web.SecurityFilterChain;
|
||||
|
||||
import com.example.demo.core.configuration.Constants;
|
||||
import com.example.demo.entrysData.api.EntrysDataSignupController;
|
||||
import com.example.demo.entrysData.model.EntrysDataRole;
|
||||
|
||||
@Configuration
|
||||
@EnableWebSecurity
|
||||
public class SecurityConfiguration {
|
||||
@Bean
|
||||
SecurityFilterChain filterChain(HttpSecurity httpSecurity) throws Exception {
|
||||
httpSecurity.headers(headers -> headers.frameOptions(FrameOptionsConfig::sameOrigin));
|
||||
httpSecurity.csrf(AbstractHttpConfigurer::disable);
|
||||
httpSecurity.cors(Customizer.withDefaults());
|
||||
|
||||
httpSecurity.authorizeHttpRequests(requests -> requests
|
||||
.requestMatchers("/css/**", "/webjars/**", "/*.svg")
|
||||
.permitAll());
|
||||
|
||||
httpSecurity.authorizeHttpRequests(requests -> requests
|
||||
.requestMatchers(Constants.ADMIN_PREFIX + "/**").hasRole(EntrysDataRole.ADMIN.name())
|
||||
.requestMatchers("/h2-console/**").hasRole(EntrysDataRole.ADMIN.name())
|
||||
.requestMatchers(EntrysDataSignupController.URL).anonymous()
|
||||
.requestMatchers(Constants.LOGIN_URL).anonymous()
|
||||
.anyRequest().authenticated());
|
||||
|
||||
httpSecurity.formLogin(formLogin -> formLogin
|
||||
.loginPage(Constants.LOGIN_URL)
|
||||
.defaultSuccessUrl("/news", true));
|
||||
|
||||
httpSecurity.rememberMe(rememberMe -> rememberMe.key("uniqueAndSecret"));
|
||||
|
||||
httpSecurity.logout(logout -> logout
|
||||
.deleteCookies("JSESSIONID"));
|
||||
|
||||
return httpSecurity.build();
|
||||
}
|
||||
|
||||
@Bean
|
||||
DaoAuthenticationProvider authenticationProvider(UserDetailsService userDetailsService) {
|
||||
final DaoAuthenticationProvider authProvider = new DaoAuthenticationProvider();
|
||||
authProvider.setUserDetailsService(userDetailsService);
|
||||
authProvider.setPasswordEncoder(passwordEncoder());
|
||||
return authProvider;
|
||||
}
|
||||
|
||||
@Bean
|
||||
PasswordEncoder passwordEncoder() {
|
||||
return new BCryptPasswordEncoder();
|
||||
}
|
||||
}
|
@ -0,0 +1,64 @@
|
||||
package com.example.demo.core.security;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Set;
|
||||
|
||||
import org.springframework.security.core.GrantedAuthority;
|
||||
import org.springframework.security.core.userdetails.UserDetails;
|
||||
|
||||
import com.example.demo.entrysData.model.EntrysDataEntity;
|
||||
|
||||
public class UserPrincipal implements UserDetails {
|
||||
private final long id;
|
||||
private final String username;
|
||||
private final String password;
|
||||
private final Set<? extends GrantedAuthority> roles;
|
||||
private final boolean active;
|
||||
|
||||
public UserPrincipal(EntrysDataEntity user) {
|
||||
this.id = user.getId();
|
||||
this.username = user.getLogin();
|
||||
this.password = user.getPassword();
|
||||
this.roles = Set.of(user.getRole());
|
||||
this.active = true;
|
||||
}
|
||||
|
||||
public Long getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getUsername() {
|
||||
return username;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getPassword() {
|
||||
return password;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<? extends GrantedAuthority> getAuthorities() {
|
||||
return roles;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEnabled() {
|
||||
return active;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isAccountNonExpired() {
|
||||
return isEnabled();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isAccountNonLocked() {
|
||||
return isEnabled();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isCredentialsNonExpired() {
|
||||
return isEnabled();
|
||||
}
|
||||
}
|
@ -0,0 +1,101 @@
|
||||
package com.example.demo.department.api;
|
||||
|
||||
import org.modelmapper.ModelMapper;
|
||||
import org.springframework.stereotype.Controller;
|
||||
import org.springframework.ui.Model;
|
||||
import org.springframework.validation.BindingResult;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.ModelAttribute;
|
||||
import org.springframework.web.bind.annotation.PathVariable;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
|
||||
import com.example.demo.core.configuration.Constants;
|
||||
import com.example.demo.department.model.DepartmentEntity;
|
||||
import com.example.demo.department.service.DepartmentService;
|
||||
|
||||
import jakarta.validation.Valid;
|
||||
|
||||
@Controller
|
||||
@RequestMapping(DepartmentController.URL)
|
||||
public class DepartmentController {
|
||||
public static final String URL = Constants.ADMIN_PREFIX + "/departments";
|
||||
private static final String DEPARTMENTS_VIEW = "departments";
|
||||
private static final String DEPARTMENTS_EDIT_VIEW = "department-edit";
|
||||
private static final String DEPARTMENTS_ATTRIBUTE = "departments";
|
||||
private final DepartmentService departmentService;
|
||||
private final ModelMapper modelMapper;
|
||||
|
||||
public DepartmentController(DepartmentService departmentService, ModelMapper modelMapper) {
|
||||
this.departmentService = departmentService;
|
||||
this.modelMapper = modelMapper;
|
||||
}
|
||||
|
||||
private DepartmentDto toDto(DepartmentEntity entity) {
|
||||
return modelMapper.map(entity, DepartmentDto.class);
|
||||
}
|
||||
|
||||
private DepartmentEntity toEntity(DepartmentDto dto) {
|
||||
return modelMapper.map(dto, DepartmentEntity.class);
|
||||
}
|
||||
|
||||
@GetMapping
|
||||
public String getAll(Model model) {
|
||||
model.addAttribute(DEPARTMENTS_ATTRIBUTE,
|
||||
departmentService.getAll().stream().map(this::toDto).toList());
|
||||
return DEPARTMENTS_VIEW;
|
||||
}
|
||||
|
||||
@GetMapping("/edit/")
|
||||
public String create(
|
||||
Model model) {
|
||||
model.addAttribute(DEPARTMENTS_ATTRIBUTE, new DepartmentDto());
|
||||
return DEPARTMENTS_EDIT_VIEW;
|
||||
}
|
||||
|
||||
@PostMapping("/edit/")
|
||||
public String create(
|
||||
Model model,
|
||||
@ModelAttribute(name = DEPARTMENTS_ATTRIBUTE) @Valid DepartmentDto department,
|
||||
BindingResult bindingResult){
|
||||
if (bindingResult.hasErrors()) {
|
||||
return DEPARTMENTS_EDIT_VIEW;
|
||||
}
|
||||
departmentService.create(toEntity(department));
|
||||
return Constants.REDIRECT_VIEW + URL;
|
||||
}
|
||||
|
||||
@GetMapping("/edit/{id}")
|
||||
public String Update(
|
||||
@PathVariable(name = "id") Long id,
|
||||
Model model) {
|
||||
if (id <= 0){
|
||||
throw new IllegalArgumentException();
|
||||
}
|
||||
model.addAttribute(DEPARTMENTS_ATTRIBUTE, toDto(departmentService.get(id)));
|
||||
return DEPARTMENTS_EDIT_VIEW;
|
||||
}
|
||||
|
||||
@PostMapping("/edit/{id}")
|
||||
public String update(
|
||||
@PathVariable(name = "id") Long id,
|
||||
@ModelAttribute(name = DEPARTMENTS_ATTRIBUTE) @Valid DepartmentDto department,
|
||||
BindingResult bindingResult,
|
||||
Model model) {
|
||||
if (bindingResult.hasErrors()) {
|
||||
return DEPARTMENTS_EDIT_VIEW;
|
||||
}
|
||||
if (id <= 0) {
|
||||
throw new IllegalArgumentException();
|
||||
}
|
||||
departmentService.update(id, toEntity(department));
|
||||
return Constants.REDIRECT_VIEW + URL;
|
||||
}
|
||||
|
||||
@PostMapping("/delete/{id}")
|
||||
public String delete(
|
||||
@PathVariable(name = "id") Long id){
|
||||
departmentService.delete(id);
|
||||
return Constants.REDIRECT_VIEW + URL;
|
||||
}
|
||||
}
|
@ -0,0 +1,28 @@
|
||||
package com.example.demo.department.api;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
|
||||
public class DepartmentDto {
|
||||
@JsonProperty(access = JsonProperty.Access.READ_ONLY)
|
||||
private Long id;
|
||||
@NotNull
|
||||
private String name;
|
||||
|
||||
public Long getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(Long id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
}
|
@ -0,0 +1,47 @@
|
||||
package com.example.demo.department.model;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
import com.example.demo.core.model.BaseEntity;
|
||||
|
||||
import jakarta.persistence.Column;
|
||||
import jakarta.persistence.Entity;
|
||||
import jakarta.persistence.Table;
|
||||
|
||||
@Entity
|
||||
@Table(name = "departments")
|
||||
public class DepartmentEntity extends BaseEntity {
|
||||
@Column(nullable = false, unique = true, length = 80)
|
||||
private String name;
|
||||
|
||||
public DepartmentEntity() {
|
||||
}
|
||||
|
||||
public DepartmentEntity(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(id, name);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (this == obj)
|
||||
return true;
|
||||
if (obj == null || getClass() != obj.getClass())
|
||||
return false;
|
||||
final DepartmentEntity other = (DepartmentEntity) obj;
|
||||
return Objects.equals(other.getId(), id)
|
||||
&& Objects.equals(other.getName(), name);
|
||||
}
|
||||
}
|
@ -0,0 +1,11 @@
|
||||
package com.example.demo.department.repository;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
import org.springframework.data.repository.CrudRepository;
|
||||
|
||||
import com.example.demo.department.model.DepartmentEntity;
|
||||
|
||||
public interface DepartmentRepository extends CrudRepository<DepartmentEntity, Long> {
|
||||
Optional<DepartmentEntity> findByNameIgnoreCase(String name);
|
||||
}
|
@ -0,0 +1,62 @@
|
||||
package com.example.demo.department.service;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.stream.StreamSupport;
|
||||
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import com.example.demo.core.error.NotFoundException;
|
||||
import com.example.demo.department.model.DepartmentEntity;
|
||||
import com.example.demo.department.repository.DepartmentRepository;
|
||||
|
||||
@Service
|
||||
public class DepartmentService {
|
||||
private final DepartmentRepository repository;
|
||||
|
||||
public DepartmentService(DepartmentRepository repository) {
|
||||
this.repository = repository;
|
||||
}
|
||||
|
||||
private void checkName(String name) {
|
||||
if (repository.findByNameIgnoreCase(name).isPresent()) {
|
||||
throw new IllegalArgumentException(
|
||||
String.format("Type with name %s is already exists", name));
|
||||
}
|
||||
}
|
||||
|
||||
@Transactional(readOnly = true)
|
||||
public List<DepartmentEntity> getAll() {
|
||||
return StreamSupport.stream(repository.findAll().spliterator(), false).toList();
|
||||
}
|
||||
|
||||
@Transactional(readOnly = true)
|
||||
public DepartmentEntity get(Long id) {
|
||||
return repository.findById(id)
|
||||
.orElseThrow(() -> new NotFoundException(DepartmentEntity.class, id));
|
||||
}
|
||||
|
||||
@Transactional
|
||||
public DepartmentEntity create(DepartmentEntity entity) {
|
||||
if (entity == null) {
|
||||
throw new IllegalArgumentException("Entity is null");
|
||||
}
|
||||
checkName(entity.getName());
|
||||
return repository.save(entity);
|
||||
}
|
||||
|
||||
@Transactional
|
||||
public DepartmentEntity update(Long id, DepartmentEntity entity) {
|
||||
final DepartmentEntity existsEntity = get(id);
|
||||
checkName(entity.getName());
|
||||
existsEntity.setName(entity.getName());
|
||||
return repository.save(existsEntity);
|
||||
}
|
||||
|
||||
@Transactional
|
||||
public DepartmentEntity delete(Long id) {
|
||||
final DepartmentEntity existsEntity = get(id);
|
||||
repository.delete(existsEntity);
|
||||
return existsEntity;
|
||||
}
|
||||
}
|
@ -0,0 +1,127 @@
|
||||
package com.example.demo.directions.api;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.modelmapper.ModelMapper;
|
||||
import org.modelmapper.TypeToken;
|
||||
import org.springframework.stereotype.Controller;
|
||||
import org.springframework.ui.Model;
|
||||
import org.springframework.validation.BindingResult;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.ModelAttribute;
|
||||
import org.springframework.web.bind.annotation.PathVariable;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
import org.springframework.web.servlet.mvc.support.RedirectAttributes;
|
||||
|
||||
import com.example.demo.core.configuration.Constants;
|
||||
import com.example.demo.department.api.DepartmentDto;
|
||||
import com.example.demo.department.service.DepartmentService;
|
||||
import com.example.demo.directions.model.DirectionsEntity;
|
||||
import com.example.demo.directions.service.DirectionsService;
|
||||
|
||||
import jakarta.validation.Valid;
|
||||
|
||||
@Controller
|
||||
@RequestMapping(DirectionsAdminController.URL)
|
||||
public class DirectionsAdminController {
|
||||
public static final String URL = Constants.ADMIN_PREFIX + "/directions";
|
||||
private static final String DIRECTIONS_URL = DirectionsController.URL;
|
||||
private static final String DIRECTIONS_EDIT_VIEW = "direction-edit";
|
||||
private static final String DIRECTIONS_ATTRIBUTE = "directions";
|
||||
private static final String DEPARTMENT_ATTRIBUTE = "departments";
|
||||
private static final String PAGE_ATTRIBUTE = "page";
|
||||
private final DirectionsService directionsService;
|
||||
private final ModelMapper modelMapper;
|
||||
private final DepartmentService departmentService;
|
||||
|
||||
public DirectionsAdminController(DirectionsService directionsService, ModelMapper modelMapper,
|
||||
DepartmentService departmentService) {
|
||||
this.directionsService = directionsService;
|
||||
this.modelMapper = modelMapper;
|
||||
this.departmentService = departmentService;
|
||||
}
|
||||
|
||||
private DirectionsDto toDto(DirectionsEntity entity) {
|
||||
return modelMapper.map(entity, DirectionsDto.class);
|
||||
}
|
||||
|
||||
private DirectionsEntity toEntity(DirectionsDto dto) {
|
||||
final DirectionsEntity entity = modelMapper.map(dto, DirectionsEntity.class);
|
||||
entity.setDepartment(departmentService.get(dto.getDepartmentId()));
|
||||
return entity;
|
||||
}
|
||||
|
||||
private List<DepartmentDto> getDepartments(){
|
||||
return modelMapper.map(departmentService.getAll(), new TypeToken<List<DepartmentDto>>(){}.getType());
|
||||
}
|
||||
|
||||
@GetMapping("/edit/")
|
||||
public String create(Model model,
|
||||
@RequestParam(name = PAGE_ATTRIBUTE, defaultValue = "0") int page) {
|
||||
model.addAttribute(DIRECTIONS_ATTRIBUTE, new DirectionsDto());
|
||||
model.addAttribute(DEPARTMENT_ATTRIBUTE, getDepartments());
|
||||
model.addAttribute(PAGE_ATTRIBUTE, page);
|
||||
return DIRECTIONS_EDIT_VIEW;
|
||||
}
|
||||
|
||||
@PostMapping("/edit/")
|
||||
public String create(
|
||||
@ModelAttribute(name = DIRECTIONS_ATTRIBUTE) @Valid DirectionsDto direction,
|
||||
@RequestParam(name = PAGE_ATTRIBUTE, defaultValue = "0") int page,
|
||||
BindingResult bindingResult,
|
||||
Model model,
|
||||
RedirectAttributes redirectAttributes) {
|
||||
if (bindingResult.hasErrors()) {
|
||||
model.addAttribute(PAGE_ATTRIBUTE, page);
|
||||
return DIRECTIONS_EDIT_VIEW;
|
||||
}
|
||||
redirectAttributes.addAttribute(PAGE_ATTRIBUTE, page);
|
||||
directionsService.create(toEntity(direction));
|
||||
return Constants.REDIRECT_VIEW + DIRECTIONS_URL;
|
||||
}
|
||||
|
||||
@GetMapping("/edit/{id}")
|
||||
public String update(
|
||||
@PathVariable(name = "id") Long id,
|
||||
@RequestParam(name = PAGE_ATTRIBUTE, defaultValue = "0") int page,
|
||||
Model model) {
|
||||
if (id <= 0) {
|
||||
throw new IllegalArgumentException();
|
||||
}
|
||||
model.addAttribute(DIRECTIONS_ATTRIBUTE, toDto(directionsService.get(id)));
|
||||
model.addAttribute(DEPARTMENT_ATTRIBUTE, getDepartments());
|
||||
model.addAttribute(PAGE_ATTRIBUTE, page);
|
||||
return DIRECTIONS_EDIT_VIEW;
|
||||
}
|
||||
|
||||
@PostMapping("/edit/{id}")
|
||||
public String update(
|
||||
@PathVariable(name = "id") Long id,
|
||||
@RequestParam(name = PAGE_ATTRIBUTE, defaultValue = "0") int page,
|
||||
@ModelAttribute(name = DIRECTIONS_ATTRIBUTE) @Valid DirectionsDto direction,
|
||||
BindingResult bindingResult,
|
||||
Model model,
|
||||
RedirectAttributes redirectAttributes) {
|
||||
if (bindingResult.hasErrors()) {
|
||||
return DIRECTIONS_EDIT_VIEW;
|
||||
}
|
||||
if (id <= 0) {
|
||||
throw new IllegalArgumentException();
|
||||
}
|
||||
redirectAttributes.addAttribute(PAGE_ATTRIBUTE, page);
|
||||
directionsService.update(id, toEntity(direction));
|
||||
return Constants.REDIRECT_VIEW + DIRECTIONS_URL;
|
||||
}
|
||||
|
||||
@PostMapping("/delete/{id}")
|
||||
public String delete(
|
||||
@PathVariable(name = "id") Long id,
|
||||
@RequestParam(name = PAGE_ATTRIBUTE, defaultValue = "0") int page,
|
||||
RedirectAttributes redirectAttributes) {
|
||||
redirectAttributes.addAttribute(PAGE_ATTRIBUTE, page);
|
||||
directionsService.delete(id);
|
||||
return Constants.REDIRECT_VIEW + DIRECTIONS_URL;
|
||||
}
|
||||
}
|
@ -0,0 +1,51 @@
|
||||
package com.example.demo.directions.api;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import org.modelmapper.ModelMapper;
|
||||
import org.springframework.stereotype.Controller;
|
||||
import org.springframework.ui.Model;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
|
||||
import com.example.demo.core.api.PageAttributesMapper;
|
||||
import com.example.demo.core.configuration.Constants;
|
||||
import com.example.demo.directions.model.DirectionsGrouped;
|
||||
import com.example.demo.directions.service.DirectionsService;
|
||||
|
||||
@Controller
|
||||
@RequestMapping(DirectionsController.URL)
|
||||
public class DirectionsController {
|
||||
public static final String URL = "/directions";
|
||||
private static final String DIRECTIONS_VIEW = "directions";
|
||||
private static final String PAGE_ATTRIBUTE = "page";
|
||||
private final DirectionsService directionsService;
|
||||
private final ModelMapper modelMapper;
|
||||
|
||||
public DirectionsController(DirectionsService directionsService, ModelMapper modelMapper) {
|
||||
this.directionsService = directionsService;
|
||||
this.modelMapper = modelMapper;
|
||||
}
|
||||
|
||||
private DirectionsGroupedDto toGroupedDto(DirectionsGrouped entity){
|
||||
return modelMapper.map(entity, DirectionsGroupedDto.class);
|
||||
}
|
||||
|
||||
@GetMapping
|
||||
public String getAllWithDepartment(
|
||||
@RequestParam(name=PAGE_ATTRIBUTE, defaultValue = "0") int page,
|
||||
@RequestParam(name="reset", required = false, defaultValue = "false") String reset,
|
||||
Model model,
|
||||
String name) {
|
||||
if (reset.contains("true")){
|
||||
name = "";
|
||||
}
|
||||
final Map<String, Object> attributes = PageAttributesMapper.
|
||||
toAttributes(directionsService.getAllWithDepartment(page, Constants.DEFAULT_PAGE_SIZE, name), this::toGroupedDto);
|
||||
model.addAllAttributes(attributes);
|
||||
model.addAttribute(PAGE_ATTRIBUTE, page);
|
||||
model.addAttribute("name", name);
|
||||
return DIRECTIONS_VIEW;
|
||||
}
|
||||
}
|
@ -0,0 +1,60 @@
|
||||
package com.example.demo.directions.api;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
|
||||
import jakarta.validation.constraints.Min;
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
|
||||
public class DirectionsDto {
|
||||
@JsonProperty(access = JsonProperty.Access.READ_ONLY)
|
||||
private Long id;
|
||||
@NotBlank
|
||||
private String code;
|
||||
@NotNull
|
||||
private String name;
|
||||
@NotNull
|
||||
@Min(1)
|
||||
private Long departmentId;
|
||||
private String things;
|
||||
|
||||
public Long getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(Long id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public String getCode() {
|
||||
return code;
|
||||
}
|
||||
|
||||
public void setCode(String code) {
|
||||
this.code = code;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public Long getDepartmentId() {
|
||||
return departmentId;
|
||||
}
|
||||
|
||||
public void setDepartmentId(Long departmentId) {
|
||||
this.departmentId = departmentId;
|
||||
}
|
||||
|
||||
public String getThings() {
|
||||
return things;
|
||||
}
|
||||
|
||||
public void setThings(String things) {
|
||||
this.things = things;
|
||||
}
|
||||
}
|
@ -0,0 +1,49 @@
|
||||
package com.example.demo.directions.api;
|
||||
|
||||
public class DirectionsGroupedDto {
|
||||
private Long id;
|
||||
private String code;
|
||||
private String name;
|
||||
private String departmentName;
|
||||
private String things;
|
||||
|
||||
public Long getId(){
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(Long id){
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public String getCode() {
|
||||
return code;
|
||||
}
|
||||
|
||||
public void setCode(String code) {
|
||||
this.code = code;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public String getDepartmentName() {
|
||||
return departmentName;
|
||||
}
|
||||
|
||||
public void setDepartmentName(String departmentName) {
|
||||
this.departmentName = departmentName;
|
||||
}
|
||||
|
||||
public String getThings() {
|
||||
return things;
|
||||
}
|
||||
|
||||
public void setThings(String things) {
|
||||
this.things = things;
|
||||
}
|
||||
}
|
@ -0,0 +1,87 @@
|
||||
package com.example.demo.directions.model;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
import com.example.demo.core.model.BaseEntity;
|
||||
import com.example.demo.department.model.DepartmentEntity;
|
||||
|
||||
import jakarta.persistence.Column;
|
||||
import jakarta.persistence.Entity;
|
||||
import jakarta.persistence.JoinColumn;
|
||||
import jakarta.persistence.ManyToOne;
|
||||
import jakarta.persistence.Table;
|
||||
|
||||
@Entity
|
||||
@Table(name = "directions")
|
||||
public class DirectionsEntity extends BaseEntity {
|
||||
@Column(nullable = false, unique = true, length = 9)
|
||||
private String code;
|
||||
@Column(nullable = false, unique = false, length = 80)
|
||||
private String name;
|
||||
@ManyToOne
|
||||
@JoinColumn(name = "departmentId", nullable = false)
|
||||
private DepartmentEntity department;
|
||||
@Column(nullable = true, unique = false, length = 80)
|
||||
private String things;
|
||||
|
||||
public DirectionsEntity() {
|
||||
}
|
||||
|
||||
public DirectionsEntity(String code, String name, DepartmentEntity department, String things) {
|
||||
this.code = code;
|
||||
this.name = name;
|
||||
this.department = department;
|
||||
this.things = things;
|
||||
}
|
||||
|
||||
public String getCode() {
|
||||
return code;
|
||||
}
|
||||
|
||||
public void setCode(String code) {
|
||||
this.code = code;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public DepartmentEntity getDepartment() {
|
||||
return department;
|
||||
}
|
||||
|
||||
public void setDepartment(DepartmentEntity department) {
|
||||
this.department = department;
|
||||
}
|
||||
|
||||
public String getThings() {
|
||||
return things;
|
||||
}
|
||||
|
||||
public void setThings(String things) {
|
||||
this.things = things;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(id, code);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (this == obj)
|
||||
return true;
|
||||
if (obj == null || getClass() != obj.getClass())
|
||||
return false;
|
||||
final DirectionsEntity other = (DirectionsEntity) obj;
|
||||
return Objects.equals(other.getId(), id)
|
||||
&& Objects.equals(other.getCode(), code)
|
||||
&& Objects.equals(other.getName(), name)
|
||||
&& Objects.equals(other.getDepartment(), department)
|
||||
&& Objects.equals(other.getThings(), things);
|
||||
}
|
||||
}
|
@ -0,0 +1,9 @@
|
||||
package com.example.demo.directions.model;
|
||||
|
||||
public interface DirectionsGrouped {
|
||||
String getCode();
|
||||
String getName();
|
||||
Long getId();
|
||||
String getThings();
|
||||
String getDepartmentName();
|
||||
}
|
@ -0,0 +1,32 @@
|
||||
package com.example.demo.directions.repository;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
import org.springframework.data.domain.Page;
|
||||
import org.springframework.data.domain.Pageable;
|
||||
import org.springframework.data.jpa.repository.Query;
|
||||
import org.springframework.data.repository.CrudRepository;
|
||||
import org.springframework.data.repository.PagingAndSortingRepository;
|
||||
|
||||
import com.example.demo.directions.model.DirectionsEntity;
|
||||
import com.example.demo.directions.model.DirectionsGrouped;
|
||||
|
||||
public interface DirectionsRepository extends CrudRepository<DirectionsEntity, Long>, PagingAndSortingRepository<DirectionsEntity, Long> {
|
||||
Optional<DirectionsEntity> findByNameIgnoreCase(String name);
|
||||
|
||||
List<DirectionsEntity> findByDepartmentId(long departmentId);
|
||||
|
||||
Page<DirectionsEntity> findByDepartmentId(long departmentId, Pageable pageable);
|
||||
|
||||
Page<DirectionsEntity> findAll(Pageable pageable);
|
||||
@Query("select d.id as id, d.code as code, d.name as name, d.things as things, " +
|
||||
"d.department.name as departmentName " +
|
||||
"from DirectionsEntity d ")
|
||||
Page<DirectionsGrouped> findAllWithDepartment(Pageable pageable);
|
||||
@Query("select d.id as id, d.code as code, d.name as name, d.things as things, " +
|
||||
"d.department.name as departmentName " +
|
||||
"from DirectionsEntity d " +
|
||||
"where lower(name) like lower(concat('%', :nameS, '%'))")
|
||||
Page<DirectionsGrouped> findAllWithDepartmentSearch(Pageable pageable, String nameS);
|
||||
}
|
@ -0,0 +1,95 @@
|
||||
package com.example.demo.directions.service;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.stream.StreamSupport;
|
||||
|
||||
import org.springframework.data.domain.Page;
|
||||
import org.springframework.data.domain.PageRequest;
|
||||
import org.springframework.data.domain.Pageable;
|
||||
import org.springframework.data.domain.Sort;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import com.example.demo.core.error.NotFoundException;
|
||||
import com.example.demo.directions.model.DirectionsEntity;
|
||||
import com.example.demo.directions.model.DirectionsGrouped;
|
||||
import com.example.demo.directions.repository.DirectionsRepository;
|
||||
|
||||
@Service
|
||||
public class DirectionsService {
|
||||
private final DirectionsRepository repository;
|
||||
|
||||
private void checkName(String name) {
|
||||
if (repository.findByNameIgnoreCase(name).isPresent()) {
|
||||
throw new IllegalArgumentException(
|
||||
String.format("Type with name %s is already exists", name));
|
||||
}
|
||||
}
|
||||
|
||||
public DirectionsService(DirectionsRepository repository) {
|
||||
this.repository = repository;
|
||||
}
|
||||
|
||||
@Transactional(readOnly = true)
|
||||
public List<DirectionsEntity> getAll(Long departmentId) {
|
||||
if (departmentId <= 0L) {
|
||||
return StreamSupport.stream(repository.findAll().spliterator(), false).toList();
|
||||
} else {
|
||||
return repository.findByDepartmentId(departmentId);
|
||||
}
|
||||
}
|
||||
|
||||
@Transactional(readOnly = true)
|
||||
public Page<DirectionsEntity> getAll(Long departmentId, int page, int size) {
|
||||
final Pageable pageRequest = PageRequest.of(page, size);
|
||||
if (departmentId <= 0L) {
|
||||
return repository.findAll(pageRequest);
|
||||
} else {
|
||||
return repository.findByDepartmentId(departmentId, pageRequest);
|
||||
}
|
||||
}
|
||||
|
||||
@Transactional(readOnly = true)
|
||||
public DirectionsEntity get(Long id) {
|
||||
return repository.findById(id)
|
||||
.orElseThrow(() -> new NotFoundException(DirectionsEntity.class, id));
|
||||
}
|
||||
|
||||
@Transactional
|
||||
public DirectionsEntity create(DirectionsEntity entity) {
|
||||
if (entity == null) {
|
||||
throw new IllegalArgumentException("Entity is null");
|
||||
}
|
||||
checkName(entity.getName());
|
||||
return repository.save(entity);
|
||||
}
|
||||
|
||||
@Transactional
|
||||
public DirectionsEntity update(Long id, DirectionsEntity entity) {
|
||||
final DirectionsEntity existsEntity = get(id);
|
||||
checkName(entity.getName());
|
||||
existsEntity.setCode(entity.getCode());
|
||||
existsEntity.setName(entity.getName());
|
||||
existsEntity.setDepartment(entity.getDepartment());
|
||||
existsEntity.setThings(entity.getThings());
|
||||
return repository.save(existsEntity);
|
||||
}
|
||||
|
||||
@Transactional
|
||||
public DirectionsEntity delete(Long id) {
|
||||
final DirectionsEntity existsEntity = get(id);
|
||||
repository.delete(existsEntity);
|
||||
return existsEntity;
|
||||
}
|
||||
|
||||
@Transactional(readOnly = true)
|
||||
public Page<DirectionsGrouped> getAllWithDepartment(int page, int size, String nameS) {
|
||||
final Pageable pageRequest = PageRequest.of(page, size, Sort.by("id"));
|
||||
if(nameS == null || nameS.isEmpty()){
|
||||
return repository.findAllWithDepartment(pageRequest);
|
||||
}
|
||||
else{
|
||||
return repository.findAllWithDepartmentSearch(pageRequest, nameS);
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,166 @@
|
||||
package com.example.demo.entrysData.api;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.modelmapper.ModelMapper;
|
||||
import org.modelmapper.TypeToken;
|
||||
import org.springframework.stereotype.Controller;
|
||||
import org.springframework.ui.Model;
|
||||
import org.springframework.validation.BindingResult;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.ModelAttribute;
|
||||
import org.springframework.web.bind.annotation.PathVariable;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
import org.springframework.web.servlet.mvc.support.RedirectAttributes;
|
||||
|
||||
import com.example.demo.core.api.PageAttributesMapper;
|
||||
import com.example.demo.core.configuration.Constants;
|
||||
import com.example.demo.department.api.DepartmentDto;
|
||||
import com.example.demo.department.service.DepartmentService;
|
||||
import com.example.demo.entrysData.model.EntrysDataEntity;
|
||||
import com.example.demo.entrysData.model.EntrysDataGrouped;
|
||||
import com.example.demo.entrysData.model.EntrysDataGroupedDepartment;
|
||||
import com.example.demo.entrysData.service.EntrysDataService;
|
||||
|
||||
import jakarta.validation.Valid;
|
||||
|
||||
@Controller
|
||||
@RequestMapping(EntrysDataController.URL)
|
||||
public class EntrysDataController {
|
||||
public static final String URL = Constants.ADMIN_PREFIX + "/users";
|
||||
private static final String USERS_VIEW = "users";
|
||||
private static final String USERS_EDIT_VIEW = "user-edit";
|
||||
private static final String DEPARTMENTS_ATTRIBUTE = "departments";
|
||||
private static final String STATISTIC_VIEW = "statistic";
|
||||
private static final String USERS_ATTRIBUTE = "users";
|
||||
private static final String PAGE_ATTRIBUTE = "page";
|
||||
private final EntrysDataService entrysDataService;
|
||||
private final ModelMapper modelMapper;
|
||||
private final DepartmentService departmentService;
|
||||
|
||||
public EntrysDataController(EntrysDataService entrysDataService, ModelMapper modelMapper,
|
||||
DepartmentService departmentService) {
|
||||
this.entrysDataService = entrysDataService;
|
||||
this.modelMapper = modelMapper;
|
||||
this.departmentService = departmentService;
|
||||
}
|
||||
|
||||
private EntrysDataDto toDto(EntrysDataEntity entity){
|
||||
return modelMapper.map(entity, EntrysDataDto.class);
|
||||
}
|
||||
|
||||
private EntrysDataGroupedDepartmentDto toDto(EntrysDataGroupedDepartment entity){
|
||||
return modelMapper.map(entity, EntrysDataGroupedDepartmentDto.class);
|
||||
}
|
||||
|
||||
private EntrysDataGroupedDto toDto(EntrysDataGrouped entity){
|
||||
return modelMapper.map(entity, EntrysDataGroupedDto.class);
|
||||
}
|
||||
|
||||
private EntrysDataEntity toEntity(EntrysDataDto dto) {
|
||||
final EntrysDataEntity entity = modelMapper.map(dto, EntrysDataEntity.class);
|
||||
entity.setDepartment(departmentService.get(dto.getDepartmentId()));
|
||||
return entity;
|
||||
}
|
||||
|
||||
private List<DepartmentDto> getDepartments(){
|
||||
return modelMapper.map(departmentService.getAll(), new TypeToken<List<DepartmentDto>>(){}.getType());
|
||||
}
|
||||
|
||||
private DepartmentDto getDepartment(Long id){
|
||||
return modelMapper.map(departmentService.get(id), new TypeToken<DepartmentDto>(){}.getType());
|
||||
}
|
||||
|
||||
@GetMapping
|
||||
public String getAll(
|
||||
@RequestParam(name = PAGE_ATTRIBUTE, defaultValue = "0") int page,
|
||||
Model model){
|
||||
final Map<String, Object> attributes = PageAttributesMapper.
|
||||
toAttributes(entrysDataService.getAllWithDepartment(page, Constants.DEFAULT_PAGE_SIZE),
|
||||
this::toDto);
|
||||
model.addAllAttributes(attributes);
|
||||
model.addAttribute(PAGE_ATTRIBUTE, page);
|
||||
return USERS_VIEW;
|
||||
}
|
||||
|
||||
@GetMapping("/statistic/")
|
||||
public String statistic(Model model){
|
||||
List<EntrysDataGrouped> list = entrysDataService.getCount();
|
||||
List<EntrysDataGroupedDto> list2 = list.stream().map(this::toDto).collect(Collectors.toList());
|
||||
model.addAttribute("statisctics", list2);
|
||||
return STATISTIC_VIEW;
|
||||
}
|
||||
|
||||
@GetMapping("/edit/")
|
||||
public String create(Model model,
|
||||
@RequestParam(name = PAGE_ATTRIBUTE, defaultValue = "0") int page) {
|
||||
model.addAttribute(USERS_ATTRIBUTE, new EntrysDataDto());
|
||||
model.addAttribute(DEPARTMENTS_ATTRIBUTE, getDepartments());
|
||||
model.addAttribute(PAGE_ATTRIBUTE, page);
|
||||
return USERS_EDIT_VIEW;
|
||||
}
|
||||
|
||||
@PostMapping("/edit/")
|
||||
public String create(
|
||||
@ModelAttribute(name = USERS_ATTRIBUTE) @Valid EntrysDataDto dto,
|
||||
@RequestParam(name = PAGE_ATTRIBUTE, defaultValue = "0") int page,
|
||||
BindingResult bindingResult,
|
||||
Model model,
|
||||
RedirectAttributes redirectAttributes){
|
||||
if (bindingResult.hasErrors()) {
|
||||
model.addAttribute(PAGE_ATTRIBUTE, page);
|
||||
return USERS_EDIT_VIEW;
|
||||
}
|
||||
redirectAttributes.addAttribute(PAGE_ATTRIBUTE, page);
|
||||
entrysDataService.create(toEntity(dto));
|
||||
return Constants.REDIRECT_VIEW + URL;
|
||||
}
|
||||
|
||||
@GetMapping("/edit/{id}")
|
||||
public String update(
|
||||
@PathVariable(name = "id") Long id,
|
||||
@RequestParam(name = PAGE_ATTRIBUTE, defaultValue = "0") int page,
|
||||
Model model) {
|
||||
if (id <= 0) {
|
||||
throw new IllegalArgumentException();
|
||||
}
|
||||
EntrysDataEntity entity = entrysDataService.get(id);
|
||||
model.addAttribute(USERS_ATTRIBUTE, toDto(entity));
|
||||
model.addAttribute(DEPARTMENTS_ATTRIBUTE, getDepartments());
|
||||
model.addAttribute(PAGE_ATTRIBUTE, page);
|
||||
return USERS_EDIT_VIEW;
|
||||
}
|
||||
|
||||
@PostMapping("/edit/{id}")
|
||||
public String update(
|
||||
@PathVariable(name = "id") Long id,
|
||||
@RequestParam(name = PAGE_ATTRIBUTE, defaultValue = "0") int page,
|
||||
@ModelAttribute(name = USERS_ATTRIBUTE) @Valid EntrysDataDto dto,
|
||||
BindingResult bindingResult,
|
||||
Model model,
|
||||
RedirectAttributes redirectAttributes) {
|
||||
if (bindingResult.hasErrors()) {
|
||||
return USERS_EDIT_VIEW;
|
||||
}
|
||||
if (id <= 0) {
|
||||
throw new IllegalArgumentException();
|
||||
}
|
||||
redirectAttributes.addAttribute(PAGE_ATTRIBUTE, page);
|
||||
entrysDataService.update(id, toEntity(dto));
|
||||
return Constants.REDIRECT_VIEW + URL;
|
||||
}
|
||||
|
||||
@PostMapping("/delete/{id}")
|
||||
public String delete(
|
||||
@PathVariable(name = "id") Long id,
|
||||
@RequestParam(name = PAGE_ATTRIBUTE, defaultValue = "0") int page,
|
||||
RedirectAttributes redirectAttributes) {
|
||||
redirectAttributes.addAttribute(PAGE_ATTRIBUTE, page);
|
||||
entrysDataService.delete(id);
|
||||
return Constants.REDIRECT_VIEW + URL;
|
||||
}
|
||||
}
|
@ -0,0 +1,43 @@
|
||||
package com.example.demo.entrysData.api;
|
||||
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import jakarta.validation.constraints.Size;
|
||||
|
||||
public class EntrysDataDto {
|
||||
private Long id;
|
||||
@NotBlank
|
||||
@Size(min = 3, max = 20)
|
||||
private String login;
|
||||
@NotNull
|
||||
private Long departmentId;
|
||||
private String role;
|
||||
|
||||
public Long getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(Long id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public Long getDepartmentId(){
|
||||
return departmentId;
|
||||
}
|
||||
|
||||
public void setDepartmentId(Long departmentId){
|
||||
this.departmentId = departmentId;
|
||||
}
|
||||
|
||||
public String getLogin() {
|
||||
return login;
|
||||
}
|
||||
|
||||
public void setLogin(String login) {
|
||||
this.login = login;
|
||||
}
|
||||
|
||||
public String getRole() {
|
||||
return role;
|
||||
}
|
||||
}
|
@ -0,0 +1,40 @@
|
||||
package com.example.demo.entrysData.api;
|
||||
|
||||
public class EntrysDataGroupedDepartmentDto {
|
||||
private String departmentName;
|
||||
private Long id;
|
||||
private String login;
|
||||
private String role;
|
||||
|
||||
public Long getId(){
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(Long Id){
|
||||
this.id = Id;
|
||||
}
|
||||
|
||||
public String getLogin(){
|
||||
return login;
|
||||
}
|
||||
|
||||
public void setLogin(String Login){
|
||||
this.login = Login;
|
||||
}
|
||||
|
||||
public String getRole(){
|
||||
return role;
|
||||
}
|
||||
|
||||
public void setRole(String Role){
|
||||
this.role = Role;
|
||||
}
|
||||
|
||||
public String getDepartmentName(){
|
||||
return departmentName;
|
||||
}
|
||||
|
||||
public void setDepartmentName(String DepartmentName){
|
||||
this.departmentName = DepartmentName;
|
||||
}
|
||||
}
|
@ -0,0 +1,23 @@
|
||||
package com.example.demo.entrysData.api;
|
||||
|
||||
public class EntrysDataGroupedDto {
|
||||
private String DepartmentName;
|
||||
|
||||
private Long Count;
|
||||
|
||||
public String getDepartmentName() {
|
||||
return DepartmentName;
|
||||
}
|
||||
|
||||
public void setDepartmentName(String DepartmentName) {
|
||||
this.DepartmentName = DepartmentName;
|
||||
}
|
||||
|
||||
public Long getCount() {
|
||||
return Count;
|
||||
}
|
||||
|
||||
public void setCount(Long Count) {
|
||||
this.Count = Count;
|
||||
}
|
||||
}
|
@ -0,0 +1,78 @@
|
||||
package com.example.demo.entrysData.api;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
import org.modelmapper.ModelMapper;
|
||||
import org.modelmapper.TypeToken;
|
||||
import org.springframework.stereotype.Controller;
|
||||
import org.springframework.ui.Model;
|
||||
import org.springframework.validation.BindingResult;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.ModelAttribute;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
|
||||
import com.example.demo.core.configuration.Constants;
|
||||
import com.example.demo.department.api.DepartmentDto;
|
||||
import com.example.demo.department.service.DepartmentService;
|
||||
import com.example.demo.entrysData.model.EntrysDataEntity;
|
||||
import com.example.demo.entrysData.service.EntrysDataService;
|
||||
|
||||
import jakarta.validation.Valid;
|
||||
|
||||
@Controller
|
||||
@RequestMapping(EntrysDataSignupController.URL)
|
||||
public class EntrysDataSignupController {
|
||||
public static final String URL = "/signup";
|
||||
|
||||
private static final String SIGNUP_VIEW = "signup";
|
||||
private static final String USER_ATTRIBUTE = "user";
|
||||
private static final String DEPARTMENT_ATTRIBUTE = "departments";
|
||||
|
||||
private final DepartmentService departmentService;
|
||||
private final EntrysDataService userService;
|
||||
private final ModelMapper modelMapper;
|
||||
|
||||
public EntrysDataSignupController(
|
||||
EntrysDataService userService,
|
||||
ModelMapper modelMapper, DepartmentService departmentService) {
|
||||
this.userService = userService;
|
||||
this.modelMapper = modelMapper;
|
||||
this.departmentService = departmentService;
|
||||
}
|
||||
|
||||
private EntrysDataEntity toEntity(EntrysDataSignupDto dto) {
|
||||
final EntrysDataEntity entity = modelMapper.map(dto, EntrysDataEntity.class);
|
||||
entity.setDepartment(departmentService.get(dto.getDepartmentId()));
|
||||
return entity;
|
||||
}
|
||||
|
||||
private List<DepartmentDto> getDepartments(){
|
||||
return modelMapper.map(departmentService.getAll(), new TypeToken<List<DepartmentDto>>(){}.getType());
|
||||
}
|
||||
|
||||
@GetMapping
|
||||
public String getSignup(Model model) {
|
||||
model.addAttribute(USER_ATTRIBUTE, new EntrysDataSignupDto());
|
||||
model.addAttribute(DEPARTMENT_ATTRIBUTE, getDepartments());
|
||||
return SIGNUP_VIEW;
|
||||
}
|
||||
|
||||
@PostMapping
|
||||
public String signup(
|
||||
@ModelAttribute(name = USER_ATTRIBUTE) @Valid EntrysDataSignupDto user,
|
||||
BindingResult bindingResult,
|
||||
Model model) {
|
||||
if (bindingResult.hasErrors()) {
|
||||
return SIGNUP_VIEW;
|
||||
}
|
||||
if (!Objects.equals(user.getPassword(), user.getPasswordConfirm())) {
|
||||
bindingResult.rejectValue("password", "signup:passwords", "Пароли не совпадают.");
|
||||
model.addAttribute(USER_ATTRIBUTE, user);
|
||||
return SIGNUP_VIEW;
|
||||
}
|
||||
userService.create(toEntity(user));
|
||||
return Constants.REDIRECT_VIEW + Constants.LOGIN_URL + "?signup";
|
||||
}
|
||||
}
|
@ -0,0 +1,60 @@
|
||||
package com.example.demo.entrysData.api;
|
||||
|
||||
import jakarta.validation.constraints.Min;
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
import jakarta.validation.constraints.Size;
|
||||
|
||||
public class EntrysDataSignupDto {
|
||||
@NotBlank
|
||||
@Size(min = 3, max = 20)
|
||||
private String login;
|
||||
private Long Id;
|
||||
@Min(1)
|
||||
private Long departmentId;
|
||||
@NotBlank
|
||||
@Size(min = 3, max = 20)
|
||||
private String password;
|
||||
@NotBlank
|
||||
@Size(min = 3, max = 20)
|
||||
private String passwordConfirm;
|
||||
|
||||
public String getLogin() {
|
||||
return login;
|
||||
}
|
||||
|
||||
public void setLogin(String login) {
|
||||
this.login = login;
|
||||
}
|
||||
|
||||
public String getPassword() {
|
||||
return password;
|
||||
}
|
||||
|
||||
public Long getId(){
|
||||
return Id;
|
||||
}
|
||||
|
||||
public void setId(Long Id){
|
||||
this.Id = Id;
|
||||
}
|
||||
|
||||
public Long getDepartmentId(){
|
||||
return departmentId;
|
||||
}
|
||||
|
||||
public void setDepartmentId(Long departmentId){
|
||||
this.departmentId = departmentId;
|
||||
}
|
||||
|
||||
public void setPassword(String password) {
|
||||
this.password = password;
|
||||
}
|
||||
|
||||
public String getPasswordConfirm() {
|
||||
return passwordConfirm;
|
||||
}
|
||||
|
||||
public void setPasswordConfirm(String passwordConfirm) {
|
||||
this.passwordConfirm = passwordConfirm;
|
||||
}
|
||||
}
|
@ -0,0 +1,86 @@
|
||||
package com.example.demo.entrysData.model;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
import com.example.demo.core.model.BaseEntity;
|
||||
import com.example.demo.department.model.DepartmentEntity;
|
||||
|
||||
import jakarta.persistence.Column;
|
||||
import jakarta.persistence.Entity;
|
||||
import jakarta.persistence.JoinColumn;
|
||||
import jakarta.persistence.ManyToOne;
|
||||
import jakarta.persistence.Table;
|
||||
|
||||
@Entity
|
||||
@Table(name = "entrysData")
|
||||
public class EntrysDataEntity extends BaseEntity {
|
||||
@Column(nullable = false, unique = true, length = 30)
|
||||
private String login;
|
||||
@Column(nullable = false, length = 60)
|
||||
private String password;
|
||||
private EntrysDataRole role;
|
||||
@ManyToOne
|
||||
@JoinColumn(name = "departmentId", nullable = true)
|
||||
private DepartmentEntity department;
|
||||
|
||||
public EntrysDataEntity() {
|
||||
}
|
||||
|
||||
public EntrysDataEntity(String login, String password, DepartmentEntity department) {
|
||||
this.login = login;
|
||||
this.password = password;
|
||||
this.role = EntrysDataRole.USER;
|
||||
this.department = department;
|
||||
}
|
||||
|
||||
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 EntrysDataRole getRole() {
|
||||
return role;
|
||||
}
|
||||
|
||||
public void setRole(EntrysDataRole role){
|
||||
this.role = role;
|
||||
}
|
||||
|
||||
public DepartmentEntity getDepartment() {
|
||||
return department;
|
||||
}
|
||||
|
||||
public void setDepartment(DepartmentEntity department) {
|
||||
this.department = department;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(id, login, password, role, department);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (this == obj)
|
||||
return true;
|
||||
if (obj == null || getClass() != obj.getClass())
|
||||
return false;
|
||||
final EntrysDataEntity other = (EntrysDataEntity) obj;
|
||||
return Objects.equals(other.getId(), id)
|
||||
&& Objects.equals(other.getLogin(), login)
|
||||
&& Objects.equals(other.getPassword(), password)
|
||||
&& Objects.equals(other.getRole(), role)
|
||||
&& Objects.equals(other.getDepartment(), department);
|
||||
}
|
||||
}
|
@ -0,0 +1,7 @@
|
||||
package com.example.demo.entrysData.model;
|
||||
|
||||
public interface EntrysDataGrouped {
|
||||
String getDepartmentName();
|
||||
|
||||
Long getCount();
|
||||
}
|
@ -0,0 +1,11 @@
|
||||
package com.example.demo.entrysData.model;
|
||||
|
||||
public interface EntrysDataGroupedDepartment {
|
||||
Long getId();
|
||||
|
||||
String getLogin();
|
||||
|
||||
String getRole();
|
||||
|
||||
String getDepartmentName();
|
||||
}
|
@ -0,0 +1,15 @@
|
||||
package com.example.demo.entrysData.model;
|
||||
|
||||
import org.springframework.security.core.GrantedAuthority;
|
||||
|
||||
public enum EntrysDataRole implements GrantedAuthority {
|
||||
ADMIN,
|
||||
USER;
|
||||
|
||||
private static final String PREFIX = "ROLE_";
|
||||
|
||||
@Override
|
||||
public String getAuthority() {
|
||||
return PREFIX + this.name();
|
||||
}
|
||||
}
|
@ -0,0 +1,35 @@
|
||||
package com.example.demo.entrysData.repository;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
import org.springframework.data.domain.Page;
|
||||
import org.springframework.data.domain.Pageable;
|
||||
import org.springframework.data.jpa.repository.Query;
|
||||
import org.springframework.data.repository.CrudRepository;
|
||||
import org.springframework.stereotype.Repository;
|
||||
|
||||
import com.example.demo.entrysData.model.EntrysDataEntity;
|
||||
import com.example.demo.entrysData.model.EntrysDataGrouped;
|
||||
import com.example.demo.entrysData.model.EntrysDataGroupedDepartment;
|
||||
|
||||
@Repository
|
||||
public interface EntrysDataRepository extends CrudRepository<EntrysDataEntity, Long> {
|
||||
Optional<EntrysDataEntity> findByloginIgnoreCase(String name);
|
||||
|
||||
List<EntrysDataEntity> findByDepartmentId(long departmentId);
|
||||
|
||||
Page<EntrysDataEntity> findByDepartmentId(long departmentId, Pageable pageable);
|
||||
|
||||
Page<EntrysDataEntity> findAll(Pageable pageable);
|
||||
|
||||
@Query("SELECT e.department.name AS departmentName, COUNT(e.id) AS count " +
|
||||
"FROM EntrysDataEntity e " +
|
||||
"GROUP BY departmentName ORDER BY count DESC")
|
||||
List<EntrysDataGrouped> getCountEntrysInDepartment();
|
||||
|
||||
@Query("SELECT e.department.name AS departmentName, e.login AS login, e.role AS role, e.id AS id " +
|
||||
"FROM EntrysDataEntity e " +
|
||||
"ORDER BY id DESC")
|
||||
Page<EntrysDataGroupedDepartment> findAllWithDepartment(Pageable pageable);
|
||||
}
|
@ -0,0 +1,125 @@
|
||||
package com.example.demo.entrysData.service;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.stream.StreamSupport;
|
||||
|
||||
import org.springframework.data.domain.Page;
|
||||
import org.springframework.data.domain.PageRequest;
|
||||
import org.springframework.data.domain.Pageable;
|
||||
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 com.example.demo.core.configuration.Constants;
|
||||
import com.example.demo.core.error.NotFoundException;
|
||||
import com.example.demo.core.security.UserPrincipal;
|
||||
import com.example.demo.entrysData.model.EntrysDataEntity;
|
||||
import com.example.demo.entrysData.model.EntrysDataGrouped;
|
||||
import com.example.demo.entrysData.model.EntrysDataGroupedDepartment;
|
||||
import com.example.demo.entrysData.model.EntrysDataRole;
|
||||
import com.example.demo.entrysData.repository.EntrysDataRepository;
|
||||
|
||||
@Service
|
||||
public class EntrysDataService implements UserDetailsService{
|
||||
private final EntrysDataRepository repository;
|
||||
private final PasswordEncoder passwordEncoder;
|
||||
|
||||
public EntrysDataService(EntrysDataRepository repository, PasswordEncoder passwordEncoder) {
|
||||
this.repository = repository;
|
||||
this.passwordEncoder = passwordEncoder;
|
||||
}
|
||||
|
||||
private void checklogin(Long id, String login) {
|
||||
final Optional<EntrysDataEntity> existsUser = repository.findByloginIgnoreCase(login);
|
||||
if (existsUser.isPresent() && !existsUser.get().getId().equals(id)) {
|
||||
throw new IllegalArgumentException(
|
||||
String.format("User with login %s is already exists", login));
|
||||
}
|
||||
}
|
||||
|
||||
@Transactional(readOnly = true)
|
||||
public List<EntrysDataEntity> getAll(Long departmentId) {
|
||||
if (departmentId <= 0L) {
|
||||
return StreamSupport.stream(repository.findAll().spliterator(), false).toList();
|
||||
} else {
|
||||
return repository.findByDepartmentId(departmentId);
|
||||
}
|
||||
}
|
||||
|
||||
@Transactional(readOnly = true)
|
||||
public Page<EntrysDataEntity> getAll(Long departmentId, int page, int size) {
|
||||
final Pageable pageRequest = PageRequest.of(page, size);
|
||||
if (departmentId <= 0L) {
|
||||
return repository.findAll(pageRequest);
|
||||
} else {
|
||||
return repository.findByDepartmentId(departmentId, pageRequest);
|
||||
}
|
||||
}
|
||||
|
||||
@Transactional(readOnly = true)
|
||||
public EntrysDataEntity get(Long id) {
|
||||
return repository.findById(id)
|
||||
.orElseThrow(() -> new NotFoundException(EntrysDataEntity.class, id));
|
||||
}
|
||||
|
||||
@Transactional
|
||||
public EntrysDataEntity create(EntrysDataEntity entity) {
|
||||
if (entity == null) {
|
||||
throw new IllegalArgumentException("Entity is null");
|
||||
}
|
||||
checklogin(null, entity.getLogin());
|
||||
final String password = Optional.ofNullable(entity.getPassword()).orElse("");
|
||||
entity.setPassword(
|
||||
passwordEncoder.encode(
|
||||
StringUtils.hasText(password.strip()) ? password : Constants.DEFAULT_PASSWORD));
|
||||
entity.setRole(Optional.ofNullable(entity.getRole()).orElse(EntrysDataRole.USER));
|
||||
repository.save(entity);
|
||||
return repository.save(entity);
|
||||
}
|
||||
|
||||
@Transactional
|
||||
public EntrysDataEntity update(Long id, EntrysDataEntity entity) {
|
||||
final EntrysDataEntity existsEntity = get(id);
|
||||
checklogin(id, entity.getLogin());
|
||||
existsEntity.setLogin(entity.getLogin());
|
||||
repository.save(existsEntity);
|
||||
return repository.save(existsEntity);
|
||||
}
|
||||
|
||||
@Transactional
|
||||
public EntrysDataEntity delete(Long id) {
|
||||
final EntrysDataEntity existsEntity = get(id);
|
||||
repository.delete(existsEntity);
|
||||
return existsEntity;
|
||||
}
|
||||
|
||||
@Transactional
|
||||
public List<EntrysDataGrouped> getCount() {
|
||||
List<EntrysDataGrouped> list = repository.getCountEntrysInDepartment();
|
||||
return list;
|
||||
}
|
||||
|
||||
@Transactional
|
||||
public Page<EntrysDataGroupedDepartment> getAllWithDepartment(int page, int size) {
|
||||
final Pageable pageRequest = PageRequest.of(page, size);
|
||||
return repository.findAllWithDepartment(pageRequest);
|
||||
}
|
||||
|
||||
@Transactional(readOnly = true)
|
||||
public EntrysDataEntity getByLogin(String login) {
|
||||
return repository.findByloginIgnoreCase(login)
|
||||
.orElseThrow(() -> new IllegalArgumentException("Invalid login"));
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(readOnly = true)
|
||||
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
|
||||
final EntrysDataEntity existsUser = getByLogin(username);
|
||||
return new UserPrincipal(existsUser);
|
||||
}
|
||||
}
|
109
src/main/java/com/example/demo/news/api/NewsAdminController.java
Normal file
109
src/main/java/com/example/demo/news/api/NewsAdminController.java
Normal file
@ -0,0 +1,109 @@
|
||||
package com.example.demo.news.api;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.modelmapper.ModelMapper;
|
||||
import org.modelmapper.TypeToken;
|
||||
import org.springframework.stereotype.Controller;
|
||||
import org.springframework.ui.Model;
|
||||
import org.springframework.validation.BindingResult;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.ModelAttribute;
|
||||
import org.springframework.web.bind.annotation.PathVariable;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
|
||||
import com.example.demo.core.configuration.Constants;
|
||||
import com.example.demo.department.api.DepartmentDto;
|
||||
import com.example.demo.department.service.DepartmentService;
|
||||
import com.example.demo.news.model.NewsEntity;
|
||||
import com.example.demo.news.service.NewsService;
|
||||
|
||||
import jakarta.validation.Valid;
|
||||
|
||||
@Controller
|
||||
@RequestMapping(NewsAdminController.URL)
|
||||
public class NewsAdminController {
|
||||
public static final String URL = Constants.ADMIN_PREFIX + "/news";
|
||||
private static final String NEWS_URL = NewsController.URL;
|
||||
private static final String NEWS_EDIT_VIEW = "new-edit";
|
||||
private static final String NEWS_ATTRIBUTE = "news";
|
||||
private static final String DEPARTMENT_ATTRIBUTE = "departments";
|
||||
private final NewsService newsService;
|
||||
private final ModelMapper modelMapper;
|
||||
private final DepartmentService departmentService;
|
||||
|
||||
public NewsAdminController(NewsService newsService, ModelMapper modelMapper, DepartmentService departmentService) {
|
||||
this.newsService = newsService;
|
||||
this.modelMapper = modelMapper;
|
||||
this.departmentService = departmentService;
|
||||
}
|
||||
|
||||
private NewsDto toDto(NewsEntity entity) {
|
||||
return modelMapper.map(entity, NewsDto.class);
|
||||
}
|
||||
|
||||
private NewsEntity toEntity(NewsDto dto) {
|
||||
final NewsEntity entity = modelMapper.map(dto, NewsEntity.class);
|
||||
entity.setDepartment(departmentService.get(dto.getDepartmentId()));
|
||||
return entity;
|
||||
}
|
||||
|
||||
private List<DepartmentDto> getDepartments(){
|
||||
return modelMapper.map(departmentService.getAll(), new TypeToken<List<DepartmentDto>>(){}.getType());
|
||||
}
|
||||
|
||||
@GetMapping("/edit/")
|
||||
public String create(Model model) {
|
||||
model.addAttribute(NEWS_ATTRIBUTE, new NewsDto());
|
||||
model.addAttribute(DEPARTMENT_ATTRIBUTE, getDepartments());
|
||||
return NEWS_EDIT_VIEW;
|
||||
}
|
||||
|
||||
@PostMapping("/edit/")
|
||||
public String create(
|
||||
@ModelAttribute(name = NEWS_ATTRIBUTE) @Valid NewsDto newItem,
|
||||
BindingResult bindingResult,
|
||||
Model model) {
|
||||
if (bindingResult.hasErrors()) {
|
||||
return NEWS_EDIT_VIEW;
|
||||
}
|
||||
newsService.create(toEntity(newItem));
|
||||
return Constants.REDIRECT_VIEW + NEWS_URL;
|
||||
}
|
||||
|
||||
@PostMapping("/delete/{id}")
|
||||
public String delete(
|
||||
@PathVariable(name = "id") Long id) {
|
||||
newsService.delete(id);
|
||||
return Constants.REDIRECT_VIEW + NEWS_URL;
|
||||
}
|
||||
|
||||
@GetMapping("/edit/{id}")
|
||||
public String update(
|
||||
@PathVariable(name = "id") Long id,
|
||||
Model model) {
|
||||
if (id <= 0) {
|
||||
throw new IllegalArgumentException();
|
||||
}
|
||||
model.addAttribute(NEWS_ATTRIBUTE, toDto(newsService.get(id)));
|
||||
model.addAttribute(DEPARTMENT_ATTRIBUTE, getDepartments());
|
||||
return NEWS_EDIT_VIEW;
|
||||
}
|
||||
|
||||
@PostMapping("/edit/{id}")
|
||||
public String update(
|
||||
@PathVariable(name = "id") Long id,
|
||||
@ModelAttribute(name = NEWS_ATTRIBUTE) @Valid NewsDto newItem,
|
||||
BindingResult bindingResult,
|
||||
Model model) {
|
||||
if (bindingResult.hasErrors()) {
|
||||
return NEWS_EDIT_VIEW;
|
||||
}
|
||||
if (id <= 0) {
|
||||
throw new IllegalArgumentException();
|
||||
}
|
||||
newsService.update(id, toEntity(newItem));
|
||||
return Constants.REDIRECT_VIEW + NEWS_URL;
|
||||
}
|
||||
}
|
71
src/main/java/com/example/demo/news/api/NewsController.java
Normal file
71
src/main/java/com/example/demo/news/api/NewsController.java
Normal file
@ -0,0 +1,71 @@
|
||||
package com.example.demo.news.api;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.modelmapper.ModelMapper;
|
||||
import org.modelmapper.TypeToken;
|
||||
import org.springframework.stereotype.Controller;
|
||||
import org.springframework.ui.Model;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
|
||||
import com.example.demo.core.api.PageAttributesMapper;
|
||||
import com.example.demo.core.configuration.Constants;
|
||||
import com.example.demo.department.api.DepartmentDto;
|
||||
import com.example.demo.department.service.DepartmentService;
|
||||
import com.example.demo.news.model.NewsEntity;
|
||||
import com.example.demo.news.service.NewsService;
|
||||
|
||||
@Controller
|
||||
@RequestMapping(NewsController.URL)
|
||||
public class NewsController {
|
||||
public static final String URL = "/news";
|
||||
private static final String NEWS_VIEW = "news";
|
||||
private static final String PAGE_ATTRIBUTE = "page";
|
||||
private static final String DEPARTMENT_ATTRIBUTE = "departments";
|
||||
private static final String DEPARTMENT_ITEM_ATTRIBUTE = "department";
|
||||
private static final String DESCRIPTION_ATTRIBUTE = "description";
|
||||
private final NewsService newsService;
|
||||
private final ModelMapper modelMapper;
|
||||
private final DepartmentService departmentService;
|
||||
|
||||
public NewsController(NewsService newsService, ModelMapper modelMapper, DepartmentService departmentService) {
|
||||
this.newsService = newsService;
|
||||
this.modelMapper = modelMapper;
|
||||
this.departmentService = departmentService;
|
||||
}
|
||||
|
||||
private NewsDto toDto(NewsEntity entity) {
|
||||
return modelMapper.map(entity, NewsDto.class);
|
||||
}
|
||||
|
||||
private List<DepartmentDto> getDepartments(){
|
||||
return modelMapper.map(departmentService.getAll(), new TypeToken<List<DepartmentDto>>(){}.getType());
|
||||
}
|
||||
|
||||
@GetMapping
|
||||
public String getAll(
|
||||
@RequestParam(name=PAGE_ATTRIBUTE, defaultValue = "0") int page,
|
||||
@RequestParam(name="departmentId", required = false) Long departmentId,
|
||||
@RequestParam(name=DESCRIPTION_ATTRIBUTE, required = false) String description,
|
||||
@RequestParam(name="reset", required = false, defaultValue = "false") String reset,
|
||||
Model model) {
|
||||
if (reset.contains("true")){
|
||||
description = "";
|
||||
departmentId = null;
|
||||
}
|
||||
final Map<String, Object> attributes = PageAttributesMapper.
|
||||
toAttributes(newsService.getAll(page, Constants.DEFAULT_PAGE_SIZE_FOR_NEWS, departmentId, description), this::toDto);
|
||||
model.addAllAttributes(attributes);
|
||||
model.addAttribute(PAGE_ATTRIBUTE, page);
|
||||
model.addAttribute(DESCRIPTION_ATTRIBUTE, description);
|
||||
if(departmentId != null){
|
||||
model.addAttribute("departmentId", departmentId);
|
||||
model.addAttribute(DEPARTMENT_ITEM_ATTRIBUTE, departmentService.get(departmentId));
|
||||
}
|
||||
model.addAttribute(DEPARTMENT_ATTRIBUTE, getDepartments());
|
||||
return NEWS_VIEW;
|
||||
}
|
||||
}
|
60
src/main/java/com/example/demo/news/api/NewsDto.java
Normal file
60
src/main/java/com/example/demo/news/api/NewsDto.java
Normal file
@ -0,0 +1,60 @@
|
||||
package com.example.demo.news.api;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
|
||||
import jakarta.validation.constraints.Min;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
|
||||
public class NewsDto {
|
||||
@JsonProperty(access = JsonProperty.Access.READ_ONLY)
|
||||
private Long id;
|
||||
@NotNull
|
||||
private String name;
|
||||
@NotNull
|
||||
private String date;
|
||||
@NotNull
|
||||
private String description;
|
||||
@NotNull
|
||||
@Min(1)
|
||||
private Long departmentId;
|
||||
|
||||
public Long getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(Long id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public String getDate() {
|
||||
return date;
|
||||
}
|
||||
|
||||
public void setDate(String date) {
|
||||
this.date = date;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public String getDescription() {
|
||||
return description;
|
||||
}
|
||||
|
||||
public void setDescription(String description) {
|
||||
this.description = description;
|
||||
}
|
||||
|
||||
public Long getDepartmentId() {
|
||||
return departmentId;
|
||||
}
|
||||
|
||||
public void setDepartmentId(Long departmentId) {
|
||||
this.departmentId = departmentId;
|
||||
}
|
||||
}
|
87
src/main/java/com/example/demo/news/model/NewsEntity.java
Normal file
87
src/main/java/com/example/demo/news/model/NewsEntity.java
Normal file
@ -0,0 +1,87 @@
|
||||
package com.example.demo.news.model;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
import com.example.demo.core.model.BaseEntity;
|
||||
import com.example.demo.department.model.DepartmentEntity;
|
||||
|
||||
import jakarta.persistence.Column;
|
||||
import jakarta.persistence.Entity;
|
||||
import jakarta.persistence.JoinColumn;
|
||||
import jakarta.persistence.ManyToOne;
|
||||
import jakarta.persistence.Table;
|
||||
|
||||
@Entity
|
||||
@Table(name = "news")
|
||||
public class NewsEntity extends BaseEntity {
|
||||
@Column(nullable = false, unique = false, length = 10)
|
||||
private String date;
|
||||
@Column(nullable = false, unique = false, length = 15)
|
||||
private String name;
|
||||
@Column(nullable = false, unique = false, length = 80)
|
||||
private String description;
|
||||
@ManyToOne
|
||||
@JoinColumn(name = "departmentId", nullable = false)
|
||||
private DepartmentEntity department;
|
||||
|
||||
public NewsEntity() {
|
||||
}
|
||||
|
||||
public NewsEntity(String date, String name, String description, DepartmentEntity department) {
|
||||
this.date = date;
|
||||
this.name = name;
|
||||
this.description = description;
|
||||
this.department = department;
|
||||
}
|
||||
|
||||
public String getDate() {
|
||||
return date;
|
||||
}
|
||||
|
||||
public void setDate(String date) {
|
||||
this.date = date;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public String getDescription() {
|
||||
return description;
|
||||
}
|
||||
|
||||
public void setDescription(String description) {
|
||||
this.description = description;
|
||||
}
|
||||
|
||||
public DepartmentEntity getDepartment() {
|
||||
return department;
|
||||
}
|
||||
|
||||
public void setDepartment(DepartmentEntity department) {
|
||||
this.department = department;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(id, name, description, date, department);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (this == obj)
|
||||
return true;
|
||||
if (obj == null || getClass() != obj.getClass())
|
||||
return false;
|
||||
final NewsEntity other = (NewsEntity) obj;
|
||||
return Objects.equals(other.getId(), id)
|
||||
&& Objects.equals(other.getDate(), date)
|
||||
&& Objects.equals(other.getName(), name)
|
||||
&& Objects.equals(other.getDescription(), description)
|
||||
&& Objects.equals(other.getDepartment(), department);
|
||||
}
|
||||
}
|
@ -0,0 +1,34 @@
|
||||
package com.example.demo.news.repository;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
import org.springframework.data.domain.Page;
|
||||
import org.springframework.data.domain.Pageable;
|
||||
import org.springframework.data.jpa.repository.Query;
|
||||
import org.springframework.data.repository.CrudRepository;
|
||||
import org.springframework.data.repository.PagingAndSortingRepository;
|
||||
import org.springframework.stereotype.Repository;
|
||||
|
||||
import com.example.demo.news.model.NewsEntity;
|
||||
|
||||
@Repository
|
||||
public interface NewsRepository extends CrudRepository<NewsEntity, Long>,
|
||||
PagingAndSortingRepository<NewsEntity, Long> {
|
||||
|
||||
Optional<NewsEntity> findByNameIgnoreCase(String name);
|
||||
|
||||
Page<NewsEntity> findAll(Pageable pageable);
|
||||
|
||||
Page<NewsEntity> findByDepartmentId(Pageable pageable, long departmentId);
|
||||
|
||||
@Query("select n " +
|
||||
"from NewsEntity n " +
|
||||
"where lower(n.description) like lower(Concat('%', :description, '%'))")
|
||||
Page<NewsEntity> findWithSearch(Pageable pageable, String description);
|
||||
|
||||
@Query("select n " +
|
||||
"from NewsEntity n " +
|
||||
"where lower(n.description) like lower(Concat('%', :description, '%')) " +
|
||||
"and n.department.Id = :departmentId")
|
||||
Page<NewsEntity> findByDepartmentIdSearch(Pageable pageable, Long departmentId, String description);
|
||||
}
|
73
src/main/java/com/example/demo/news/service/NewsService.java
Normal file
73
src/main/java/com/example/demo/news/service/NewsService.java
Normal file
@ -0,0 +1,73 @@
|
||||
package com.example.demo.news.service;
|
||||
|
||||
import org.springframework.data.domain.Page;
|
||||
import org.springframework.data.domain.PageRequest;
|
||||
import org.springframework.data.domain.Pageable;
|
||||
import org.springframework.data.domain.Sort;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import com.example.demo.core.error.NotFoundException;
|
||||
import com.example.demo.news.model.NewsEntity;
|
||||
import com.example.demo.news.repository.NewsRepository;
|
||||
|
||||
@Service
|
||||
public class NewsService {
|
||||
private final NewsRepository repository;
|
||||
|
||||
public NewsService(NewsRepository repository) {
|
||||
this.repository = repository;
|
||||
}
|
||||
|
||||
private void checkName(String name) {
|
||||
if (repository.findByNameIgnoreCase(name).isPresent()) {
|
||||
throw new IllegalArgumentException(
|
||||
String.format("Type with name %s is already exists", name));
|
||||
}
|
||||
}
|
||||
|
||||
@Transactional(readOnly = true)
|
||||
public Page<NewsEntity> getAll(int page, int size, Long departmentId, String description){
|
||||
final Pageable pageRequest = PageRequest.of(page, size, Sort.by("id"));
|
||||
|
||||
if(departmentId != null && (description == null || description.isEmpty())){
|
||||
return repository.findByDepartmentId(pageRequest, departmentId);
|
||||
}
|
||||
else if(departmentId == null && description != null){
|
||||
return repository.findWithSearch(pageRequest, description);
|
||||
}
|
||||
else if(departmentId != null && description != null){
|
||||
return repository.findByDepartmentIdSearch(pageRequest, departmentId, description);
|
||||
}
|
||||
return repository.findAll(pageRequest);
|
||||
}
|
||||
|
||||
@Transactional(readOnly = true)
|
||||
public NewsEntity get(Long id) {
|
||||
return repository.findById(id)
|
||||
.orElseThrow(() -> new NotFoundException(NewsEntity.class, id));
|
||||
}
|
||||
|
||||
@Transactional
|
||||
public NewsEntity create(NewsEntity entity) {
|
||||
checkName(entity.getName());
|
||||
return repository.save(entity);
|
||||
}
|
||||
|
||||
@Transactional
|
||||
public NewsEntity update(Long id, NewsEntity entity) {
|
||||
final NewsEntity existsEntity = get(id);
|
||||
existsEntity.setDate(entity.getDate());
|
||||
existsEntity.setName(entity.getName());
|
||||
existsEntity.setDescription(entity.getDescription());
|
||||
existsEntity.setDepartment(entity.getDepartment());
|
||||
return repository.save(existsEntity);
|
||||
}
|
||||
|
||||
@Transactional
|
||||
public NewsEntity delete(Long id) {
|
||||
final NewsEntity existsEntity = get(id);
|
||||
repository.delete(existsEntity);
|
||||
return existsEntity;
|
||||
}
|
||||
}
|
20
src/main/resources/application.properties
Normal file
20
src/main/resources/application.properties
Normal file
@ -0,0 +1,20 @@
|
||||
# Server
|
||||
spring.main.banner-mode=off
|
||||
server.port=8080
|
||||
|
||||
# Logger settings
|
||||
# Available levels are: TRACE, DEBUG, INFO, WARN, ERROR, FATAL, OFF
|
||||
logging.level.com.example.demo=DEBUG
|
||||
|
||||
# JPA Settings
|
||||
spring.datasource.url=jdbc:h2:file:./data
|
||||
spring.datasource.username=sa
|
||||
spring.datasource.password=password
|
||||
spring.datasource.driver-class-name=org.h2.Driver
|
||||
spring.jpa.hibernate.ddl-auto=update
|
||||
spring.jpa.open-in-view=false
|
||||
spring.jpa.show-sql=true
|
||||
spring.jpa.properties.hibernate.format_sql=true
|
||||
|
||||
# H2 console
|
||||
spring.h2.console.enabled=true
|
198
src/main/resources/public/css/style.css
Normal file
198
src/main/resources/public/css/style.css
Normal file
@ -0,0 +1,198 @@
|
||||
html,
|
||||
body {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
h1 {
|
||||
font-size: 1.5em;
|
||||
}
|
||||
|
||||
h2 {
|
||||
font-size: 1.25em;
|
||||
}
|
||||
|
||||
h3 {
|
||||
font-size: 1.1em;
|
||||
}
|
||||
|
||||
td form {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
margin-top: -.25em;
|
||||
}
|
||||
|
||||
.button-fixed-width {
|
||||
width: 150px;
|
||||
}
|
||||
|
||||
.button-link {
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.invalid-feedback {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.w-10 {
|
||||
width: 10% !important;
|
||||
}
|
||||
|
||||
.my-navbar {
|
||||
background-color: #3c3c3c !important;
|
||||
}
|
||||
|
||||
.my-navbar .link a:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
.my-navbar .logo {
|
||||
width: 26px;
|
||||
height: 26px;
|
||||
}
|
||||
|
||||
.my-footer {
|
||||
background-color: #2c2c2c;
|
||||
height: 32px;
|
||||
color: rgba(255, 255, 255, 0.5);
|
||||
}
|
||||
|
||||
.cart-image {
|
||||
width: 3.1rem;
|
||||
padding: 0.25rem;
|
||||
border-radius: 0.5rem;
|
||||
}
|
||||
|
||||
.cart-item {
|
||||
height: auto;
|
||||
}
|
||||
|
||||
.headStyle {
|
||||
color: #FFFFFF
|
||||
}
|
||||
|
||||
.headStyleNavBar {
|
||||
background-color: #454545;
|
||||
}
|
||||
|
||||
.headStyleNavBar2 {
|
||||
background-color: #575757
|
||||
}
|
||||
|
||||
.stsp {
|
||||
font-size: 30px;
|
||||
color: #333333;
|
||||
text-align: center;
|
||||
margin-top: 70;
|
||||
}
|
||||
|
||||
.mainSt {
|
||||
color: #008B8B;
|
||||
font-size: 50px;
|
||||
}
|
||||
|
||||
.rectPage2 {
|
||||
color: #FFFFFF;
|
||||
width: 1470px;
|
||||
height: 900px;
|
||||
border: 2px solid #000000;
|
||||
background-color: #FFFFFF;
|
||||
opacity: 0.8;
|
||||
}
|
||||
|
||||
.rectPage4 {
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
color: #FFFFFF;
|
||||
width: 550px;
|
||||
height: 300px;
|
||||
border: 2px solid #000000;
|
||||
background-color: #7c7474;
|
||||
opacity: 0.8;
|
||||
}
|
||||
|
||||
.rectPage5 {
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
margin-top: auto;
|
||||
margin-bottom: auto;
|
||||
width: 600px;
|
||||
height: 500px;
|
||||
border: 2px solid #000000;
|
||||
background-color: #FFFFFF;
|
||||
opacity: 0.7;
|
||||
}
|
||||
|
||||
.stylePage2 {
|
||||
float: center;
|
||||
margin-right: 7;
|
||||
color: #063638;
|
||||
font-size: 18px
|
||||
}
|
||||
|
||||
.styleParagraph {
|
||||
border-top: 2px solid #000000;
|
||||
}
|
||||
|
||||
.styleBlack {
|
||||
color: #000000;
|
||||
}
|
||||
|
||||
.stylePage2LargeSymbol {
|
||||
float: left;
|
||||
margin-right: 7;
|
||||
color: #118D94;
|
||||
font-size: 50px;
|
||||
line-height: 52px
|
||||
}
|
||||
|
||||
.forButtons {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
width: 400px;
|
||||
}
|
||||
|
||||
.forButtons form:first-child {
|
||||
order: 1;
|
||||
}
|
||||
|
||||
.forButtons form:last-child {
|
||||
order: 2;
|
||||
}
|
||||
|
||||
.colStyle {
|
||||
margin-left: 12%;
|
||||
margin-top: 9px;
|
||||
}
|
||||
|
||||
.rectNews {
|
||||
width: 400px;
|
||||
height: 270px;
|
||||
border-radius: 8%;
|
||||
border: 2px solid #2582A3;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.rectNewsTextBox {
|
||||
width: 400px;
|
||||
min-height: 50px;
|
||||
border: 2px solid #000000;
|
||||
background-color: #FFFFFF;
|
||||
opacity: 0.7;
|
||||
border-radius: 10% / 40%;
|
||||
margin-top: 5px;
|
||||
}
|
||||
|
||||
.rectNewsText {
|
||||
color: #000000;
|
||||
font-size: 15px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 3px;
|
||||
}
|
||||
|
||||
.imageNew {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
object-fit: cover;
|
||||
}
|
BIN
src/main/resources/public/images/New1.png
Normal file
BIN
src/main/resources/public/images/New1.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 268 KiB |
BIN
src/main/resources/public/images/New2.png
Normal file
BIN
src/main/resources/public/images/New2.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 208 KiB |
BIN
src/main/resources/public/images/New3.png
Normal file
BIN
src/main/resources/public/images/New3.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 185 KiB |
BIN
src/main/resources/public/images/img1.jpg
Normal file
BIN
src/main/resources/public/images/img1.jpg
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.1 MiB |
BIN
src/main/resources/public/images/logo.png
Normal file
BIN
src/main/resources/public/images/logo.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 13 KiB |
67
src/main/resources/templates/default.html
Normal file
67
src/main/resources/templates/default.html
Normal file
@ -0,0 +1,67 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="ru" data-bs-theme="dark" xmlns:th="http://www.thymeleaf.org"
|
||||
xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout">
|
||||
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<link rel="icon" href="/images/logo.png" type="image/x-icon">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<script type="text/javascript" src="/webjars/bootstrap/5.3.3/dist/js/bootstrap.bundle.min.js"></script>
|
||||
<link rel="stylesheet" href="/webjars/bootstrap/5.3.3/dist/css/bootstrap.min.css" />
|
||||
<link rel="stylesheet" href="/webjars/bootstrap-icons/1.11.3/font/bootstrap-icons.min.css" />
|
||||
<link rel="stylesheet" href="/webjars/font-awesome/4.7.0/css/font-awesome.min.css">
|
||||
<link rel="stylesheet" href="/css/style.css" />
|
||||
</head>
|
||||
|
||||
<body class="h-100 d-flex flex-column">
|
||||
<nav class="navbar navbar-expand-md my-navbar" data-bs-theme="dark">
|
||||
<div class="container-fluid">
|
||||
<a class="navbar-brand" href="/news">
|
||||
<i></i>
|
||||
UlSTU
|
||||
</a>
|
||||
<th:block sec:authorize="isAuthenticated()" th:with="userName=${#authentication.name}">
|
||||
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#main-navbar"
|
||||
aria-controls="main-navbar" aria-expanded="false" aria-label="Toggle navigation">
|
||||
<span class="navbar-toggler-icon"></span>
|
||||
</button>
|
||||
<div class="collapse navbar-collapse" id="main-navbar">
|
||||
<ul class="navbar-nav me-auto link" th:with="activeLink=${#objects.nullSafe(servletPath, '')}">
|
||||
<a class="nav-link" href="/news"
|
||||
th:classappend="${activeLink.startsWith('/news') ? 'active' : ''}">
|
||||
Новости
|
||||
</a>
|
||||
<a class="nav-link" href="/directions"
|
||||
th:classappend="${activeLink.startsWith('/directions') ? 'active' : ''}">
|
||||
Направления
|
||||
</a>
|
||||
<th:block sec:authorize="hasRole('ADMIN')">
|
||||
<a class="nav-link" href="/admin/departments"
|
||||
th:classappend="${activeLink.startsWith('/departments') ? 'active' : ''}">
|
||||
Кафедры
|
||||
</a>
|
||||
<a class="nav-link" href="/admin/users"
|
||||
th:classappend="${activeLink.startsWith('/users') ? 'active' : ''}">
|
||||
Пользователи
|
||||
</a>
|
||||
</th:block>
|
||||
</ul>
|
||||
<ul class="navbar-nav" th:if="${not #strings.isEmpty(userName)}">
|
||||
<form th:action="@{/logout}" method="post">
|
||||
<button type="submit" class="navbar-brand nav-link" onclick="return confirm('Вы уверены?')">
|
||||
Выход ([[${userName}]])
|
||||
</button>
|
||||
</form>
|
||||
</ul>
|
||||
</div>
|
||||
</th:block>
|
||||
</div>
|
||||
</nav>
|
||||
<main class="container-fluid p-2" layout:fragment="content">
|
||||
</main>
|
||||
<footer class="my-footer mt-auto d-flex flex-shrink-0 justify-content-center align-items-center">
|
||||
Адреса: ул. Северный Венец, 32; ул. Андрея Блаженного, 3
|
||||
</footer>
|
||||
</body>
|
||||
|
||||
</html>
|
29
src/main/resources/templates/department-edit.html
Normal file
29
src/main/resources/templates/department-edit.html
Normal file
@ -0,0 +1,29 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="ru" xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout" layout:decorate="~{default}">
|
||||
|
||||
<head>
|
||||
<title>Редактировать кафедру</title>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<main layout:fragment="content">
|
||||
<form action="#" th:action="@{/admin/departments/edit/{id}(id=${departments.id})}" th:object="${departments}"
|
||||
method="post">
|
||||
<div class="mb-3">
|
||||
<label for="id" class="form-label">ID</label>
|
||||
<input type="text" th:value="*{id}" id="id" class="form-control" readonly disabled>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label for="name" class="form-label">Название</label>
|
||||
<input type="text" th:field="*{name}" id="name" class="form-control">
|
||||
<div th:if="${#fields.hasErrors('name')}" th:errors="*{name}" class="invalid-feedback"></div>
|
||||
</div>
|
||||
<div class="mb-3 d-flex flex-row">
|
||||
<button class="btn btn-primary me-2 button-fixed-width" type="submit">Сохранить</button>
|
||||
<a class="btn btn-secondary button-fixed-width" href="/admin/departments">Отмена</a>
|
||||
</div>
|
||||
</form>
|
||||
</main>
|
||||
</body>
|
||||
|
||||
</html>
|
59
src/main/resources/templates/departments.html
Normal file
59
src/main/resources/templates/departments.html
Normal file
@ -0,0 +1,59 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="ru" xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout" layout:decorate="~{default}">
|
||||
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>Новости</title>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<main class="text-center" layout:fragment="content">
|
||||
<span class="mainSt">
|
||||
<b>Кафедры</b>
|
||||
</span>
|
||||
<div>
|
||||
<a href="/admin/departments/edit/" class="btn btn-danger">Добавить кафедру</a>
|
||||
</div>
|
||||
<th:block th:switch="${departments.size()}">
|
||||
<h2 th:case="0">Данные отсутствуют</h2>
|
||||
<th:block th:case="*">
|
||||
|
||||
<table class="table">
|
||||
<caption></caption>
|
||||
<thead>
|
||||
<tr>
|
||||
<th scope="col" class="w-10">ID</th>
|
||||
<th scope="col" class="w-auto">Название</th>
|
||||
<th scope="col" class="w-10"></th>
|
||||
<th scope="col" class="w-10"></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr th:each="department : ${departments}">
|
||||
<th scope="row" th:text="${department.id}"></th>
|
||||
<td th:text="${department.name}"></td>
|
||||
<td>
|
||||
<form th:action="@{/admin/departments/edit/{id}(id=${department.id})}" method="get">
|
||||
<button type="submit" class="btn btn-link button-link">
|
||||
<i class="fa fa-edit">Изменить</i>
|
||||
</button>
|
||||
</form>
|
||||
</td>
|
||||
<td>
|
||||
<form th:action="@{/admin/departments/delete/{id}(id=${department.id})}" method="post">
|
||||
<button type="submit" class="btn btn-link button-link"
|
||||
onclick="return confirm('Вы уверены?')">
|
||||
<i class="fa fa-trash">Удалить</i>
|
||||
</button>
|
||||
</form>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</th:block>
|
||||
</th:block>
|
||||
</main>
|
||||
</body>
|
||||
|
||||
</html>
|
53
src/main/resources/templates/direction-edit.html
Normal file
53
src/main/resources/templates/direction-edit.html
Normal file
@ -0,0 +1,53 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="ru" xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout" layout:decorate="~{default}">
|
||||
|
||||
<head>
|
||||
<title>Редактировать направление</title>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<main layout:fragment="content">
|
||||
<form action="#" th:action="@{/admin/directions/edit/{id}(id=${directions.id},(page=${page}))}"
|
||||
th:object="${directions}" method="post">
|
||||
<div class="mb-3">
|
||||
<label for="id" class="form-label">ID</label>
|
||||
<input type="text" th:value="*{id}" id="id" class="form-control" readonly disabled>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label for="name" class="form-label">Направление</label>
|
||||
<input type="text" th:field="*{name}" id="name" class="form-control">
|
||||
<div th:if="${#fields.hasErrors('name')}" th:errors="*{name}" class="invalid-feedback"></div>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label for="code" class="form-label">Код</label>
|
||||
<input type="text" th:field="*{code}" id="code" class="form-control">
|
||||
<div th:if="${#fields.hasErrors('code')}" th:errors="*{code}" class="invalid-feedback">
|
||||
</div>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label for="things" class="form-label">Предметы ЕГЭ</label>
|
||||
<input type="text" th:field="*{things}" id="things" class="form-control">
|
||||
<div th:if="${#fields.hasErrors('things')}" th:errors="*{things}" class="invalid-feedback">
|
||||
</div>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label for="departmentId" class="form-label">Кафедра</label>
|
||||
<select th:field="*{departmentId}" id="departmentId" class="form-control">
|
||||
<option th:if="*{departmentId} == null" value="">Выберите кафедру</option>
|
||||
<option th:unless="*{departmentId} == null" th:value="*{departmentId}" th:text="*{name}"></option>
|
||||
<option th:each=" department : ${departments}" th:value="${department.id}"
|
||||
th:text="${department.name}" th:selected="*{departmentId} == ${department.id}">
|
||||
</option>
|
||||
</select>
|
||||
<div th:if="${#fields.hasErrors('departmentId')}" th:errors="*{departmentId}" class="invalid-feedback">
|
||||
</div>
|
||||
</div>
|
||||
<div class="mb-3 d-flex flex-row">
|
||||
<button class="btn btn-primary me-2 button-fixed-width" type="submit">Сохранить</button>
|
||||
<a class="btn btn-secondary button-fixed-width" th:href="@{/directions(page=${page})}">Отмена</a>
|
||||
</div>
|
||||
</form>
|
||||
</main>
|
||||
</body>
|
||||
|
||||
</html>
|
92
src/main/resources/templates/directions.html
Normal file
92
src/main/resources/templates/directions.html
Normal file
@ -0,0 +1,92 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="ru" xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout" layout:decorate="~{default}">
|
||||
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>Новости</title>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<main class="text-center" layout:fragment="content">
|
||||
<label class="mainSt d-flex justify-content-center" for="direction">
|
||||
<b>Направления</b>
|
||||
</label>
|
||||
<div class="d-flex justify-content-center">
|
||||
<form class="w-25" method="get">
|
||||
<button class = "btn button-link" name="reset" value="true" type="submit">
|
||||
<i class = "fa fa-refresh">Сброс</i>
|
||||
</button>
|
||||
<div class="input-group mt-1">
|
||||
<input class="form-control" type="text" name="name" th:value="${name}"
|
||||
placeholder="Поиск направлений..." />
|
||||
<button class="btn btn-primary" type="submit">
|
||||
<i class="fa fa-search">Поиск</i>
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
<th:block sec:authorize="hasRole('ADMIN')">
|
||||
<div>
|
||||
<a th:href="@{/admin/directions/edit/(page=${page})}" class="btn btn-danger mt-4">Добавить
|
||||
направление</a>
|
||||
</div>
|
||||
</th:block>
|
||||
<th:block th:switch="${items.size()}">
|
||||
<h2 th:case="0">Данные отсутствуют</h2>
|
||||
<th:block th:case="*">
|
||||
<table class="table">
|
||||
<caption></caption>
|
||||
<thead>
|
||||
<tr>
|
||||
<th scope="col" class="w-5">ID</th>
|
||||
<th scope="col" class="w-10">Код</th>
|
||||
<th scope="col" class="w-25">Направление</th>
|
||||
<th scope="col" class="w-25">Кафедра</th>
|
||||
<th scope="col" class="w-25">Предметы ЕГЭ</th>
|
||||
<th:block sec:authorize="hasRole('ADMIN')">
|
||||
<th scope="col" class="w-5"></th>
|
||||
<th scope="col" class="w-5"></th>
|
||||
</th:block>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr th:each="direction : ${items}">
|
||||
<th scope="row" th:text="${direction.id}"></th>
|
||||
<td th:text="${direction.code}"></td>
|
||||
<td th:text="${direction.name}"></td>
|
||||
<td th:text="${direction.departmentName}"></td>
|
||||
<td th:text="${direction.things}"></td>
|
||||
<th:block sec:authorize="hasRole('ADMIN')">
|
||||
<td>
|
||||
<form th:action="@{/admin/directions/edit/{id}(id=${direction.id})}" method="get">
|
||||
<input type="hidden" th:name="page" th:value="${page}">
|
||||
<button type="submit" class="btn btn-link button-link">
|
||||
<i class="fa fa-edit">Изменить</i>
|
||||
</button>
|
||||
</form>
|
||||
</td>
|
||||
<td>
|
||||
<form th:action="@{/admin/directions/delete/{id}(id=${direction.id})}"
|
||||
method="post">
|
||||
<input type="hidden" th:name="page" th:value="${page}">
|
||||
<button type="submit" class="btn btn-link button-link"
|
||||
onclick="return confirm('Вы уверены?')">
|
||||
<i class="fa fa-trash">Удалить</i>
|
||||
</button>
|
||||
</form>
|
||||
</td>
|
||||
</th:block>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</th:block>
|
||||
<th:block th:replace="~{ pagination :: pagination (
|
||||
url=${'directions'},
|
||||
totalPages=${totalPages},
|
||||
currentPage=${currentPage}) }" />
|
||||
</th:block>
|
||||
</main>
|
||||
</body>
|
||||
|
||||
</html>
|
37
src/main/resources/templates/error.html
Normal file
37
src/main/resources/templates/error.html
Normal file
@ -0,0 +1,37 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="ru" xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout" layout:decorate="~{default}">
|
||||
|
||||
<head>
|
||||
<title>Ошибка</title>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<main layout:fragment="content">
|
||||
<ul class="list-group mb-2">
|
||||
<th:block th:if="${#strings.isEmpty(message)}">
|
||||
<li class="list-group-item">
|
||||
Неизвестная ошибка
|
||||
</li>
|
||||
</th:block>
|
||||
<th:block th:if="${not #strings.isEmpty(message)}">
|
||||
<li class="list-group-item">
|
||||
<strong>Ошибка:</strong> [[${message}]]
|
||||
</li>
|
||||
</th:block>
|
||||
<th:block th:if="${not #strings.isEmpty(url)}">
|
||||
<li class="list-group-item">
|
||||
<strong>Адрес:</strong> [[${url}]]
|
||||
</li>
|
||||
<li class="list-group-item">
|
||||
<strong>Класс исключения:</strong> [[${exception}]]
|
||||
</li>
|
||||
<li class="list-group-item">
|
||||
[[${method}]] ([[${file}]]:[[${line}]])
|
||||
</li>
|
||||
</th:block>
|
||||
</ul>
|
||||
<a class="btn btn-primary button-fixed-width" href="/news">На главную</a>
|
||||
</main>
|
||||
</body>
|
||||
|
||||
</html>
|
44
src/main/resources/templates/login.html
Normal file
44
src/main/resources/templates/login.html
Normal file
@ -0,0 +1,44 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="ru" xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout" layout:decorate="~{default}">
|
||||
|
||||
<head>
|
||||
<title>Вход</title>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<main layout:fragment="content">
|
||||
<form action="#" th:action="@{/login}" method="post">
|
||||
<div th:if="${param.error}" class="alert alert-danger">
|
||||
Неверный логин или пароль
|
||||
</div>
|
||||
<div th:if="${param.logout}" class="alert alert-success">
|
||||
Выход успешно произведен
|
||||
</div>
|
||||
<div th:if="${param.signup}" class="alert alert-success">
|
||||
Пользователь успешно создан
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label for="username" class="form-label">Имя пользователя</label>
|
||||
<input type="text" id="username" name="username" class="form-control" required minlength="3"
|
||||
maxlength="20">
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label for="password" class="form-label">Пароль</label>
|
||||
<input type="password" id="password" name="password" class="form-control" required minlength="3"
|
||||
maxlength="20">
|
||||
</div>
|
||||
<div class="form-check mb-3">
|
||||
<input class="form-check-input" type="checkbox" id="remember-me" name="remember-me" checked>
|
||||
<label class="form-check-label" for="remember-me">Запомнить меня</label>
|
||||
</div>
|
||||
<div class="mb-3 d-flex flex-row">
|
||||
<button class="btn btn-primary me-2 button-fixed-width" type="submit">Войти</button>
|
||||
<a class="btn btn-secondary button-fixed-width" href="/signup">Регистрация</a>
|
||||
</div>
|
||||
</form>
|
||||
</main>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
|
||||
</html>
|
53
src/main/resources/templates/new-edit.html
Normal file
53
src/main/resources/templates/new-edit.html
Normal file
@ -0,0 +1,53 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="ru" xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout" layout:decorate="~{default}">
|
||||
|
||||
<head>
|
||||
<title>Редактировать новость</title>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<main layout:fragment="content">
|
||||
<form action="#" th:action="@{/admin/news/edit/{id}(id=${news.id},(page=${page}))}" th:object="${news}"
|
||||
method="post">
|
||||
<div class="mb-3">
|
||||
<label for="id" class="form-label">ID</label>
|
||||
<input type="text" th:value="*{id}" id="id" class="form-control" readonly disabled>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label for="name" class="form-label">Заголовок</label>
|
||||
<input type="text" th:field="*{name}" id="name" class="form-control">
|
||||
<div th:if="${#fields.hasErrors('name')}" th:errors="*{name}" class="invalid-feedback"></div>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label for="description" class="form-label">Описание</label>
|
||||
<input type="text" th:field="*{description}" id="description" class="form-control">
|
||||
<div th:if="${#fields.hasErrors('description')}" th:errors="*{description}" class="invalid-feedback">
|
||||
</div>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label for="date" class="form-label">Дата</label>
|
||||
<input type="date" th:field="*{date}" id="date" class="form-control">
|
||||
<div th:if="${#fields.hasErrors('date')}" th:errors="*{date}" class="invalid-feedback">
|
||||
</div>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label for="departmentId" class="form-label">Кафедра</label>
|
||||
<select th:field="*{departmentId}" id="departmentId" class="form-control">
|
||||
<option th:if="*{departmentId} == null" value="">Выберите кафедру</option>
|
||||
<option th:unless="*{departmentId} == null" th:value="*{departmentId}" th:text="*{name}"></option>
|
||||
<option th:each=" department : ${departments}" th:value="${department.id}"
|
||||
th:text="${department.name}" th:selected="*{departmentId} == ${department.id}">
|
||||
</option>
|
||||
</select>
|
||||
<div th:if="${#fields.hasErrors('departmentId')}" th:errors="*{departmentId}" class="invalid-feedback">
|
||||
</div>
|
||||
</div>
|
||||
<div class="mb-3 d-flex flex-row">
|
||||
<button class="btn btn-primary me-2 button-fixed-width" type="submit">Сохранить</button>
|
||||
<a class="btn btn-secondary button-fixed-width" th:href="@{/news(page=${page})}">Отмена</a>
|
||||
</div>
|
||||
</form>
|
||||
</main>
|
||||
</body>
|
||||
|
||||
</html>
|
91
src/main/resources/templates/news.html
Normal file
91
src/main/resources/templates/news.html
Normal file
@ -0,0 +1,91 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="ru" xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout" layout:decorate="~{default}">
|
||||
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>Новости</title>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<main class="text-center" layout:fragment="content">
|
||||
<span class="mainSt">
|
||||
<b>Новости</b>
|
||||
</span>
|
||||
<div class="d-flex justify-content-center">
|
||||
<form class="w-25" method="get">
|
||||
<button class = "btn button-link" name="reset" value="true" type="submit">
|
||||
<i class = "fa fa-refresh">Сброс</i>
|
||||
</button>
|
||||
<div class="input-group mt-1">
|
||||
<input class="form-control" type="text" name="description" th:value="${description}"
|
||||
placeholder="Поиск новостей..." />
|
||||
<button class="btn btn-primary" type="submit">
|
||||
<i class="fa fa-search">Поиск</i>
|
||||
</button>
|
||||
</div>
|
||||
<div class="input-group d-flex justify-content-center mt-2">
|
||||
<div class="input-group-append">
|
||||
<label for="departmentId" class="form-label" />
|
||||
<select id="departmentId" class="form-control" name="departmentId">
|
||||
<option th:if="${departmentId} == null" value="" selected>Без фильтра</option>
|
||||
<option th:if="${departmentId} != null" value="">Без фильтра</option>
|
||||
<option th:each="departmentItem : ${departments}" th:value="${departmentItem.id}"
|
||||
th:text="${departmentItem.name}" th:selected="${departmentId} == ${departmentItem.id}">
|
||||
</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="input-group-append">
|
||||
<button class="btn btn-primary" type="submit">
|
||||
<i class="fa fa-check">Выбрать</i>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
<th:block sec:authorize="hasRole('ADMIN')">
|
||||
<div>
|
||||
<a th:href="@{/admin/news/edit/(page=${page})}" class="btn btn-danger mt-1">Добавить новость</a>
|
||||
</div>
|
||||
</th:block>
|
||||
<th:block th:switch="${items.size()}">
|
||||
<h2 th:case="0">Данные отсутствуют</h2>
|
||||
<th:block th:case="*">
|
||||
<div class="row">
|
||||
<div class="col mt-4 colStyle" th:each="newItem : ${items}">
|
||||
<div class="rectNews d-flex flex-column">
|
||||
<img class="imageNew" src="./images/img1.jpg" alt th:alt="${newItem.name}" width="100%">
|
||||
</div>
|
||||
<div class="rectNewsTextBox">
|
||||
<span class="rectNewsText">
|
||||
<b scope="row" th:text="${newItem.description}"></b>
|
||||
</span>
|
||||
</div>
|
||||
<th:block sec:authorize="hasRole('ADMIN')">
|
||||
<div class="forButtons">
|
||||
<form th:action="@{/admin/news/edit/{id}(id=${newItem.id})}" method="get">
|
||||
<input type="hidden" th:name="page" th:value="${page}">
|
||||
<button type="submit" class="btn btn-link button-link">
|
||||
<i class="fa fa-edit">Изменить</i>
|
||||
</form>
|
||||
<form th:action="@{/admin/news/delete/{id}(id=${newItem.id})}" method="post">
|
||||
<input type="hidden" th:name="page" th:value="${page}">
|
||||
<button type="submit" class="btn btn-link button-link"
|
||||
onclick="return confirm('Вы уверены?')">
|
||||
<i class="fa fa-trash">Удалить</i>
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
</th:block>
|
||||
</div>
|
||||
</div>
|
||||
</th:block>
|
||||
<th:block th:replace="~{ pagination :: pagination (
|
||||
url=${'news'},
|
||||
totalPages=${totalPages},
|
||||
currentPage=${currentPage}) }" />
|
||||
</th:block>
|
||||
</main>
|
||||
</body>
|
||||
|
||||
</html>
|
51
src/main/resources/templates/pagination.html
Normal file
51
src/main/resources/templates/pagination.html
Normal file
@ -0,0 +1,51 @@
|
||||
<!DOCTYPE html>
|
||||
<html xmlns:th="http://www.thymeleaf.org">
|
||||
|
||||
<body>
|
||||
|
||||
<th:block th:fragment="pagination (url, totalPages, currentPage)">
|
||||
<nav th:if="${totalPages > 1}" th:with="
|
||||
maxPage=2,
|
||||
currentPage=${currentPage + 1}">
|
||||
<ul class="pagination justify-content-center"
|
||||
th:with="
|
||||
seqFrom=${currentPage - maxPage < 1 ? 1 : currentPage - maxPage},
|
||||
seqTo=${currentPage + maxPage > totalPages ? totalPages : currentPage + maxPage}">
|
||||
<th:block th:if="${currentPage > maxPage + 1}">
|
||||
<li class="page-item">
|
||||
<a class="page-link" aria-label="Previous" th:href="@{/{url}?page=0(url=${url})}">
|
||||
<span aria-hidden="true">«</span>
|
||||
</a>
|
||||
</li>
|
||||
<li class="page-item disabled">
|
||||
<span class="page-link" aria-label="Previous">
|
||||
<span aria-hidden="true">…</span>
|
||||
</span>
|
||||
</li>
|
||||
</th:block>
|
||||
<li class="page-item" th:each="page : ${#numbers.sequence(seqFrom, seqTo)}"
|
||||
th:classappend="${page == currentPage} ? 'active' : ''">
|
||||
<a class=" page-link" th:href="@{/{url}?page={page}(url=${url},page=${page - 1})}">
|
||||
<span th:text="${page}" />
|
||||
</a>
|
||||
</li>
|
||||
<th:block th:if="${currentPage < totalPages - maxPage}">
|
||||
<li class="page-item disabled">
|
||||
<span class="page-link" aria-label="Previous">
|
||||
<span aria-hidden="true">…</span>
|
||||
</span>
|
||||
</li>
|
||||
<li class="page-item">
|
||||
<a class="page-link" aria-label="Next"
|
||||
th:href="@{/{url}?page={page}(url=${url},page=${totalPages - 1})}">
|
||||
<span aria-hidden="true">»</span>
|
||||
</a>
|
||||
</li>
|
||||
</th:block>
|
||||
</ul>
|
||||
</nav>
|
||||
</th:block>
|
||||
|
||||
</body>
|
||||
|
||||
</html>
|
49
src/main/resources/templates/signup.html
Normal file
49
src/main/resources/templates/signup.html
Normal file
@ -0,0 +1,49 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="ru" xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout" layout:decorate="~{default}">
|
||||
|
||||
<head>
|
||||
<title>Вход</title>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<main layout:fragment="content">
|
||||
<form action="#" th:action="@{/signup}" th:object="${user}" method="post">
|
||||
<div class="mb-3">
|
||||
<label for="login" class="form-label">Имя пользователя</label>
|
||||
<input type="text" th:field="*{login}" id="login" class="form-control">
|
||||
<div th:if="${#fields.hasErrors('login')}" th:errors="*{login}" class="invalid-feedback"></div>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label for="departmentId" class="form-label">Кафедра</label>
|
||||
<select th:field="*{departmentId}" id="departmentId" class="form-control">
|
||||
<option th:if="*{departmentId} == null" value="">Выберите кафедру</option>
|
||||
<option th:unless="*{departmentId} == null" th:value="*{departmentId}" th:text="*{name}"></option>
|
||||
<option th:each=" department : ${departments}" th:value="${department.id}"
|
||||
th:text="${department.name}" th:selected="*{departmentId} == ${department.id}">
|
||||
</option>
|
||||
</select>
|
||||
<div th:if="${#fields.hasErrors('departmentId')}" th:errors="*{departmentId}" class="invalid-feedback">
|
||||
</div>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label for="password" class="form-label">Пароль</label>
|
||||
<input type="password" th:field="*{password}" id="password" class="form-control">
|
||||
<div th:if="${#fields.hasErrors('password')}" th:errors="*{password}" class="invalid-feedback"></div>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label for="passwordConfirm" class="form-label">Пароль (подтверждение)</label>
|
||||
<input type="password" th:field="*{passwordConfirm}" id="passwordConfirm" class="form-control">
|
||||
<div th:if="${#fields.hasErrors('passwordConfirm')}" th:errors="*{passwordConfirm}"
|
||||
class="invalid-feedback"></div>
|
||||
</div>
|
||||
<div class="mb-3 d-flex flex-row">
|
||||
<button class="btn btn-primary me-2 button-fixed-width" type="submit">Регистрация</button>
|
||||
<a class="btn btn-secondary button-fixed-width" href="/">Отмена</a>
|
||||
</div>
|
||||
</form>
|
||||
</main>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
|
||||
</html>
|
26
src/main/resources/templates/statistic.html
Normal file
26
src/main/resources/templates/statistic.html
Normal file
@ -0,0 +1,26 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="ru" xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout" layout:decorate="~{default}">
|
||||
<head>
|
||||
<title>Статистика пользователей</title>
|
||||
</head>
|
||||
<body>
|
||||
<main layout:fragment="content">
|
||||
<table class="table">
|
||||
<caption></caption>
|
||||
<thead>
|
||||
<tr>
|
||||
<th scope="col" class="w-50">Кафедра</th>
|
||||
<th scope="col" class="w-25">Количество пользователей</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr th:each="statisctic : ${statisctics}">
|
||||
<th scope="row" th:text="${statisctic.departmentName}"></th>
|
||||
<th scope="row" th:text="${statisctic.count}"></th>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</main>
|
||||
</body>
|
||||
|
||||
</html>
|
50
src/main/resources/templates/user-edit.html
Normal file
50
src/main/resources/templates/user-edit.html
Normal file
@ -0,0 +1,50 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="ru" xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout" layout:decorate="~{default}">
|
||||
|
||||
<head>
|
||||
<title>Редактировать пользователя</title>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<main layout:fragment="content">
|
||||
<form action="#" th:action="@{/users/edit/{id}(id=${users.id},(page=${page}))}"
|
||||
th:object="${users}" method="post">
|
||||
<div class="mb-3">
|
||||
<label for="id" class="form-label">ID</label>
|
||||
<input type="text" th:value="*{id}" id="id" class="form-control" readonly disabled>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label for="role" class="form-label">Роль</label>
|
||||
<input type="text" th:field="*{role}" id="role" class="form-control" readonly disabled>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label for="login" class="form-label">Логин</label>
|
||||
<input type="text" th:field="*{login}" id="login" class="form-control">
|
||||
<div th:if="${#fields.hasErrors('login')}" th:errors="*{login}" class="invalid-feedback"></div>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label for="password" class="form-label">Пароль</label>
|
||||
<input type="password" th:field="*{password}" id="password" class="form-control">
|
||||
<div th:if="${#fields.hasErrors('password')}" th:errors="*{password}" class="invalid-feedback">
|
||||
</div>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label for="departmentId" class="form-label">Кафедра</label>
|
||||
<select th:field="*{departmentId}" id="departmentId" class="form-control">
|
||||
<option th:if="${departmentId} == null" value="" selected>Выберите кафедру</option>
|
||||
<option th:each="departmentItem : ${departments}" th:value="${departmentItem.id}"
|
||||
th:text="${departmentItem.name}" th:selected="${departmentId} == ${departmentItem.id}">
|
||||
</option>
|
||||
</select>
|
||||
<div th:if="${#fields.hasErrors('departmentId')}" th:errors="*{departmentId}" class="invalid-feedback">
|
||||
</div>
|
||||
</div>
|
||||
<div class="mb-3 d-flex flex-row">
|
||||
<button class="btn btn-primary me-2 button-fixed-width" type="submit">Сохранить</button>
|
||||
<a class="btn btn-secondary button-fixed-width" th:href="@{/users(page=${page})}">Отмена</a>
|
||||
</div>
|
||||
</form>
|
||||
</main>
|
||||
</body>
|
||||
|
||||
</html>
|
71
src/main/resources/templates/users.html
Normal file
71
src/main/resources/templates/users.html
Normal file
@ -0,0 +1,71 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="ru" xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout" layout:decorate="~{default}">
|
||||
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>Пользователи</title>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<main class="text-center" layout:fragment="content">
|
||||
<span class="mainSt">
|
||||
<b>Пользователи</b>
|
||||
</span>
|
||||
<div>
|
||||
<a href="/users/edit/" class="btn btn-danger">Добавить пользователя</a>
|
||||
</div>
|
||||
<div class="d-flex justify-content-right">
|
||||
<a href="/admin/users/statistic/" class="btn btn-link">
|
||||
<i class="fa fa-file-earmark-bar-graph">Статистика</i>
|
||||
</a>
|
||||
</div>
|
||||
<th:block th:switch="${items.size()}">
|
||||
<h2 th:case="0">Данные отсутствуют</h2>
|
||||
<th:block th:case="*">
|
||||
<table class="table">
|
||||
<caption></caption>
|
||||
<thead>
|
||||
<tr>
|
||||
<th scope="col" class="w-5">ID</th>
|
||||
<th scope="col" class="w-10">Логин</th>
|
||||
<th scope="col" class="w-25">Роль</th>
|
||||
<th scope="col" class="w-50">Кафедра</th>
|
||||
<th scope="col" class="w-10"></th>
|
||||
<th scope="col" class="w-10"></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr th:each="user : ${items}">
|
||||
<th scope="row" th:text="${user.id}"></th>
|
||||
<td th:text="${user.login}"></td>
|
||||
<td th:text="${user.role}"></td>
|
||||
<td th:text="${user.departmentName}"></td>
|
||||
<td>
|
||||
<form th:action="@{/users/edit/{id}(id=${user.id})}" method="get">
|
||||
<button type="submit" class="btn btn-link button-link">
|
||||
<i class="fa fa-edit">Изменить</i>
|
||||
</button>
|
||||
</form>
|
||||
</td>
|
||||
<td>
|
||||
<form th:action="@{/users/delete/{id}(id=${user.id})}" method="post">
|
||||
<button type="submit" class="btn btn-link button-link"
|
||||
onclick="return confirm('Вы уверены?')">
|
||||
<i class="fa fa-trash">Удалить</i>
|
||||
</button>
|
||||
</form>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</th:block>
|
||||
<th:block th:replace="~{ pagination :: pagination (
|
||||
url=${'users'},
|
||||
totalPages=${totalPages},
|
||||
currentPage=${currentPage}) }" />
|
||||
</th:block>
|
||||
</main>
|
||||
</body>
|
||||
|
||||
</html>
|
57
src/main/resources/templates/usersGroup.html
Normal file
57
src/main/resources/templates/usersGroup.html
Normal file
@ -0,0 +1,57 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="ru" xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout" layout:decorate="~{default}">
|
||||
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>Новости</title>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<main class="text-center" layout:fragment="content">
|
||||
<span class="mainSt">
|
||||
<b>Новости</b>
|
||||
</span>
|
||||
<div>
|
||||
<a href="/news/edit/" class="btn btn-danger">Добавить новость</a>
|
||||
</div>
|
||||
<table class="table">
|
||||
<caption></caption>
|
||||
<thead>
|
||||
<tr>
|
||||
<th scope="col" class="w-5">ID</th>
|
||||
<th scope="col" class="w-10">Дата публикации</th>
|
||||
<th scope="col" class="w-25">Заголовок</th>
|
||||
<th scope="col" class="w-50">Описание</th>
|
||||
<th scope="col" class="w-10"></th>
|
||||
<th scope="col" class="w-10"></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr th:each="newItem : ${news}">
|
||||
<th scope="row" th:text="${newItem.id}"></th>
|
||||
<td th:text="${newItem.date}"></td>
|
||||
<td th:text="${newItem.name}"></td>
|
||||
<td th:text="${newItem.description}"></td>
|
||||
<td>
|
||||
<form th:action="@{/news/edit/{id}(id=${newItem.id})}" method="get">
|
||||
<button type="submit" class="btn btn-link button-link">
|
||||
<i class="fa fa-edit">Изменить</i>
|
||||
</button>
|
||||
</form>
|
||||
</td>
|
||||
<td>
|
||||
<form th:action="@{/news/delete/{id}(id=${newItem.id})}" method="post">
|
||||
<button type="submit" class="btn btn-link button-link"
|
||||
onclick="return confirm('Вы уверены?')">
|
||||
<i class="fa fa-trash">Удалить</i>
|
||||
</button>
|
||||
</form>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</main>
|
||||
</body>
|
||||
|
||||
</html>
|
Loading…
Reference in New Issue
Block a user