From 900cfdd0364ff39ae360cb811be32eb211d76bb8 Mon Sep 17 00:00:00 2001 From: Anton Romanov Date: Mon, 24 Feb 2025 21:42:35 +0400 Subject: [PATCH] Merge branch 'master' into 3-parse-rule # Conflicts: # src/main/java/ru/ulstu/fc/rule/controller/RuleController.java # src/main/java/ru/ulstu/fc/rule/model/Variable.java # src/main/java/ru/ulstu/fc/rule/repository/VariableRepository.java # src/main/java/ru/ulstu/fc/rule/service/FuzzyInferenceService.java # src/main/java/ru/ulstu/fc/rule/service/VariableService.java # src/main/resources/templates/default.html # src/main/resources/templates/listRules.html --- .gitea/workflows/test.yaml | 15 +++ .gitignore | 2 + .vscode/launch.json | 12 ++ .vscode/settings.json | 4 + Jenkinsfile | 31 +++++ README.md | 24 +++- client example.py | 78 ++++++++++++ deploy/nio17.sh | 4 + gradle/wrapper/gradle-wrapper.jar | Bin 56177 -> 43583 bytes gradle/wrapper/gradle-wrapper.properties | 4 +- open.png | Bin 0 -> 19003 bytes run.png | Bin 0 -> 36655 bytes .../ulstu/fc/FuzzyControllerApplication.java | 17 +++ .../java/ru/ulstu/fc/config/Constants.java | 12 ++ .../config/GlobalDefaultExceptionHandler.java | 15 +-- .../ru/ulstu/fc/config/MvcConfiguration.java | 28 ++--- .../config/PasswordEncoderConfiguration.java | 13 ++ .../fc/config/SecurityConfiguration.java | 51 ++++++++ .../fc/config/TemplateConfiguration.java | 8 +- .../fc/config/WebClientConfiguration.java | 13 -- .../ru/ulstu/fc/core/model/BaseEntity.java | 84 +++++++++++++ .../fc/core/model/OffsetablePageRequest.java | 119 ++++++++++++++++++ .../project/controller/ProjectController.java | 66 ++++++++++ .../controller/ProjectRestController.java | 41 ++++++ .../ru/ulstu/fc/project/model/Project.java | 53 ++++++++ .../ulstu/fc/project/model/ProjectForm.java | 38 ++++++ .../project/repository/ProjectRepository.java | 14 +++ .../project/service/ProjectRulesService.java | 27 ++++ .../fc/project/service/ProjectService.java | 64 ++++++++++ .../service/ProjectVariableService.java | 27 ++++ .../rule/controller/FuzzyTermController.java | 54 ++++++++ .../controller/InferenceMvcController.java | 44 ++++--- .../controller/InferenceRestController.java | 2 +- .../fc/rule/controller/RuleController.java | 107 +++++----------- .../rule/controller/VariableController.java | 56 +++++++++ .../ru/ulstu/fc/rule/model/Antecedent.java | 19 --- .../ru/ulstu/fc/rule/model/FuzzyRule.java | 36 ++++++ .../ru/ulstu/fc/rule/model/FuzzyRuleForm.java | 52 ++++++++ .../ru/ulstu/fc/rule/model/FuzzyTerm.java | 34 +++++ .../ru/ulstu/fc/rule/model/FuzzyTermForm.java | 71 +++++++++++ .../ru/ulstu/fc/rule/model/InferenceForm.java | 20 +-- .../java/ru/ulstu/fc/rule/model/Variable.java | 55 ++++++-- .../ru/ulstu/fc/rule/model/VariableForm.java | 61 +++++++++ .../ru/ulstu/fc/rule/model/VariableValue.java | 30 ----- .../rule/repository/FuzzyRuleRepository.java | 12 ++ .../rule/repository/FuzzyTermRepository.java | 8 ++ .../rule/repository/VariableRepository.java | 5 + .../rule/service/FuzzyInferenceService.java | 49 ++++---- .../fc/rule/service/FuzzyRuleService.java | 41 ++++++ .../fc/rule/service/FuzzyTermService.java | 56 +++++++++ .../fc/rule/service/VariableService.java | 45 ++++--- .../fc/user/controller/UserController.java | 40 ++++++ .../java/ru/ulstu/fc/user/model/User.java | 77 ++++++++++++ .../java/ru/ulstu/fc/user/model/UserDto.java | 40 ++++++ .../fc/user/model/UserNotFoundException.java | 7 ++ .../java/ru/ulstu/fc/user/model/UserRole.java | 50 ++++++++ .../fc/user/model/UserRoleConstants.java | 6 + .../ru/ulstu/fc/user/model/UserSession.java | 105 ++++++++++++++++ .../fc/user/repository/UserRepository.java | 15 +++ .../user/repository/UserRoleRepository.java | 7 ++ .../repository/UserSessionRepository.java | 13 ++ .../fc/user/service/IpAddressResolver.java | 21 ++++ .../ru/ulstu/fc/user/service/UserService.java | 93 ++++++++++++++ .../user/service/UserSessionLoginHandler.java | 43 +++++++ .../service/UserSessionLogoutHandler.java | 47 +++++++ .../fc/user/service/UserSessionService.java | 42 +++++++ .../ru/ulstu/fc/user/utils/UserUtils.java | 24 ++++ src/main/resources/application.properties | 5 +- src/main/resources/templates/default.html | 32 +++-- src/main/resources/templates/error/404.html | 13 ++ src/main/resources/templates/error/500.html | 13 ++ .../resources/templates/fuzzyTerm/edit.html | 54 ++++++++ src/main/resources/templates/index.html | 16 +-- src/main/resources/templates/listRules.html | 42 ------- src/main/resources/templates/login.html | 35 ++++++ src/main/resources/templates/loginError.html | 39 ++++++ .../resources/templates/project/edit.html | 86 +++++++++++++ .../resources/templates/project/list.html | 22 ++++ .../templates/project/listRules.html | 30 +++++ src/main/resources/templates/register.html | 29 +++++ src/main/resources/templates/rule/edit.html | 38 ++++++ .../resources/templates/variable/edit.html | 55 ++++++++ 82 files changed, 2541 insertions(+), 319 deletions(-) create mode 100644 .gitea/workflows/test.yaml create mode 100644 .vscode/launch.json create mode 100644 .vscode/settings.json create mode 100644 Jenkinsfile create mode 100644 client example.py create mode 100644 deploy/nio17.sh create mode 100644 open.png create mode 100644 run.png create mode 100644 src/main/java/ru/ulstu/fc/config/Constants.java create mode 100644 src/main/java/ru/ulstu/fc/config/PasswordEncoderConfiguration.java create mode 100644 src/main/java/ru/ulstu/fc/config/SecurityConfiguration.java delete mode 100644 src/main/java/ru/ulstu/fc/config/WebClientConfiguration.java create mode 100644 src/main/java/ru/ulstu/fc/core/model/BaseEntity.java create mode 100644 src/main/java/ru/ulstu/fc/core/model/OffsetablePageRequest.java create mode 100644 src/main/java/ru/ulstu/fc/project/controller/ProjectController.java create mode 100644 src/main/java/ru/ulstu/fc/project/controller/ProjectRestController.java create mode 100644 src/main/java/ru/ulstu/fc/project/model/Project.java create mode 100644 src/main/java/ru/ulstu/fc/project/model/ProjectForm.java create mode 100644 src/main/java/ru/ulstu/fc/project/repository/ProjectRepository.java create mode 100644 src/main/java/ru/ulstu/fc/project/service/ProjectRulesService.java create mode 100644 src/main/java/ru/ulstu/fc/project/service/ProjectService.java create mode 100644 src/main/java/ru/ulstu/fc/project/service/ProjectVariableService.java create mode 100644 src/main/java/ru/ulstu/fc/rule/controller/FuzzyTermController.java create mode 100644 src/main/java/ru/ulstu/fc/rule/controller/VariableController.java delete mode 100644 src/main/java/ru/ulstu/fc/rule/model/Antecedent.java create mode 100644 src/main/java/ru/ulstu/fc/rule/model/FuzzyRule.java create mode 100644 src/main/java/ru/ulstu/fc/rule/model/FuzzyRuleForm.java create mode 100644 src/main/java/ru/ulstu/fc/rule/model/FuzzyTerm.java create mode 100644 src/main/java/ru/ulstu/fc/rule/model/FuzzyTermForm.java create mode 100644 src/main/java/ru/ulstu/fc/rule/model/VariableForm.java delete mode 100644 src/main/java/ru/ulstu/fc/rule/model/VariableValue.java create mode 100644 src/main/java/ru/ulstu/fc/rule/repository/FuzzyRuleRepository.java create mode 100644 src/main/java/ru/ulstu/fc/rule/repository/FuzzyTermRepository.java create mode 100644 src/main/java/ru/ulstu/fc/rule/service/FuzzyRuleService.java create mode 100644 src/main/java/ru/ulstu/fc/rule/service/FuzzyTermService.java create mode 100644 src/main/java/ru/ulstu/fc/user/controller/UserController.java create mode 100644 src/main/java/ru/ulstu/fc/user/model/User.java create mode 100644 src/main/java/ru/ulstu/fc/user/model/UserDto.java create mode 100644 src/main/java/ru/ulstu/fc/user/model/UserNotFoundException.java create mode 100644 src/main/java/ru/ulstu/fc/user/model/UserRole.java create mode 100644 src/main/java/ru/ulstu/fc/user/model/UserRoleConstants.java create mode 100644 src/main/java/ru/ulstu/fc/user/model/UserSession.java create mode 100644 src/main/java/ru/ulstu/fc/user/repository/UserRepository.java create mode 100644 src/main/java/ru/ulstu/fc/user/repository/UserRoleRepository.java create mode 100644 src/main/java/ru/ulstu/fc/user/repository/UserSessionRepository.java create mode 100644 src/main/java/ru/ulstu/fc/user/service/IpAddressResolver.java create mode 100644 src/main/java/ru/ulstu/fc/user/service/UserService.java create mode 100644 src/main/java/ru/ulstu/fc/user/service/UserSessionLoginHandler.java create mode 100644 src/main/java/ru/ulstu/fc/user/service/UserSessionLogoutHandler.java create mode 100644 src/main/java/ru/ulstu/fc/user/service/UserSessionService.java create mode 100644 src/main/java/ru/ulstu/fc/user/utils/UserUtils.java create mode 100644 src/main/resources/templates/error/404.html create mode 100644 src/main/resources/templates/error/500.html create mode 100644 src/main/resources/templates/fuzzyTerm/edit.html create mode 100644 src/main/resources/templates/login.html create mode 100644 src/main/resources/templates/loginError.html create mode 100644 src/main/resources/templates/project/edit.html create mode 100644 src/main/resources/templates/project/list.html create mode 100644 src/main/resources/templates/project/listRules.html create mode 100644 src/main/resources/templates/register.html create mode 100644 src/main/resources/templates/rule/edit.html create mode 100644 src/main/resources/templates/variable/edit.html diff --git a/.gitea/workflows/test.yaml b/.gitea/workflows/test.yaml new file mode 100644 index 0000000..d93aee3 --- /dev/null +++ b/.gitea/workflows/test.yaml @@ -0,0 +1,15 @@ +name: CI fuzzy controller +on: [ push ] +jobs: + container-test-job: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - name: Set up JDK 21 for x64 + uses: actions/setup-java@v4 + with: + java-version: '21' + distribution: 'temurin' + architecture: x64 + - name: Test with Gradle + run: bash ./gradlew test \ No newline at end of file diff --git a/.gitignore b/.gitignore index b1933c3..e920055 100644 --- a/.gitignore +++ b/.gitignore @@ -2,6 +2,8 @@ # Created by https://www.toptal.com/developers/gitignore/api/intellij,java,maven,gradle,eclipse,netbeans,node # Edit at https://www.toptal.com/developers/gitignore?templates=intellij,java,maven,gradle,eclipse,netbeans,node +data/* + ### Eclipse ### .metadata bin/ diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 0000000..9fa47b3 --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,12 @@ +{ + "version": "0.2.0", + "configurations": [ + { + "type": "java", + "name": "FuzzyControllerApplication", + "request": "launch", + "mainClass": "ru.ulstu.fc.FuzzyControllerApplication", + "projectName": "fuzzy-controller" + } + ] +} \ No newline at end of file diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..3ea84cf --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,4 @@ +{ + "java.configuration.updateBuildConfiguration": "interactive", + "java.compile.nullAnalysis.mode": "disabled" +} \ No newline at end of file diff --git a/Jenkinsfile b/Jenkinsfile new file mode 100644 index 0000000..f04f27e --- /dev/null +++ b/Jenkinsfile @@ -0,0 +1,31 @@ +pipeline { + agent any + tools { + jdk 'jdk-21' + } + stages { + stage('Build') { + steps { + script { + sh "sh ./gradlew clean build" + } + } + } + stage('Deploy') { + steps { + script { + sh "bash deploy/nio17.sh &" + } + } + } + } + post { + always { + script { + if (currentBuild.currentResult == 'FAILURE') { + step([$class: 'Mailer', notifyEveryUnstableBuild: true, recipients: "a.romanov@ulstu.ru", sendToIndividuals: true]) + } + } + } + } +} \ No newline at end of file diff --git a/README.md b/README.md index 682cebb..88eea71 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,24 @@ -# fuzzy-controller +# Нечеткий контроллер +Порядок развертывания проекта разработчиками: + +1. Скачать и установить JDK (используется 21 LTS + JDK): https://download.oracle.com/java/21/latest/jdk-21_windows-x64_bin.exe + +2. Проверить установку, набрав в консоли команду "java -version" + +3. Скачать и установить IDE: IntelliJIDEA Сommunity + version https://www.jetbrains.com/ru-ru/idea/download/#section=windows + + ИЛИ + + Visual Studio Code https://code.visualstudio.com/ + +4. Скачать и открыть проект в IDE: + + ![open.png](open.png) +5. Сформировать конфигурацию для запуска: + ![run.png](run.png) +6. Запустить проект. +7. Открыть страницу в браузере http://localhost:8080/ +8. Демо: http://plans.athene.tech/ diff --git a/client example.py b/client example.py new file mode 100644 index 0000000..b910fdd --- /dev/null +++ b/client example.py @@ -0,0 +1,78 @@ +import json +import requests + +url = 'http://plans.athene.tech/rest/get-inference' +headers = { + 'Content-type': 'application/json', + 'Accept': 'application/json' +} + +age = 65 +income = 20000 + +data = { + "inputVariables": [ + { + "name": "возраст", + "values": [ + { + "fuzzyTerm": "молодой", + "value": 35 + }, + { + "fuzzyTerm": "средний", + "value": 45 + }, + { + "fuzzyTerm": "старый", + "value": 65 + }] + }, + { + "name": "доход", + "values": [ + { + "fuzzyTerm": "низкий", + "value": 50000 + }, + { + "fuzzyTerm": "средний", + "value": 100000 + }, + { + "fuzzyTerm": "высокий", + "value": 500000 + }] + }], + "outputVariable": + { + "name": "кредит", + "values": [ + { + "fuzzyTerm": "небольшой", + "value": 50000 + }, + { + "fuzzyTerm": "средний", + "value": 100000 + }, + { + "fuzzyTerm": "большой", + "value": 200000 + }] + }, + "rules": [ + "if доход is высокий and возраст is молодой then кредит is большой", + "if доход is высокий and возраст is средний then кредит is средний", + "if доход is высокий and возраст is старый then кредит is средний", + "if доход is низкий and возраст is молодой then кредит is небольшой" + ], + "values": + { + "доход": income, + "возраст": age + } +} + +response = requests.post(url, data=json.dumps(data), headers=headers) +print(response.json()) diff --git a/deploy/nio17.sh b/deploy/nio17.sh new file mode 100644 index 0000000..8334d4a --- /dev/null +++ b/deploy/nio17.sh @@ -0,0 +1,4 @@ +#!/bin/ +bash ./gradlew assemble +scp -o StrictHostKeyChecking=no build/libs/fuzzy-controller-0.0.1-SNAPSHOT.jar root@192.168.1.129:/root/fuzzy-controller.jar +ssh root@192.168.1.129 "killall java >> /dev/null && /opt/jdk-21/bin/java -jar /root/fuzzy-controller.jar >> /root/fc.log &" \ No newline at end of file diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar index 29953ea141f55e3b8fc691d31b5ca8816d89fa87..a4b76b9530d66f5e68d973ea569d8e19de379189 100644 GIT binary patch literal 43583 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 yu?e%!_ptzGB|8GrCfu%p?(_ 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 GZ~q4|mN%#X literal 56177 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 i;x|p)^~-yfPOPa3(|vBayXKz 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

hs8wbKRatGS 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 literal 0 HcmV?d00001 diff --git a/src/main/java/ru/ulstu/fc/FuzzyControllerApplication.java b/src/main/java/ru/ulstu/fc/FuzzyControllerApplication.java index f490108..27dcbf9 100644 --- a/src/main/java/ru/ulstu/fc/FuzzyControllerApplication.java +++ b/src/main/java/ru/ulstu/fc/FuzzyControllerApplication.java @@ -2,11 +2,28 @@ package ru.ulstu.fc; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.boot.context.event.ApplicationReadyEvent; +import org.springframework.boot.context.properties.EnableConfigurationProperties; +import org.springframework.context.event.EventListener; +import ru.ulstu.fc.user.service.UserService; @SpringBootApplication +@EnableConfigurationProperties public class FuzzyControllerApplication { + private final UserService userService; + + public FuzzyControllerApplication(UserService userService) { + this.userService = userService; + } + public static void main(String[] args) { SpringApplication.run(FuzzyControllerApplication.class, args); } + + @EventListener(ApplicationReadyEvent.class) + public void doSomethingAfterStartup() { + System.out.println("hello world, I have just started up"); + userService.initDefaultAdmin(); + } } diff --git a/src/main/java/ru/ulstu/fc/config/Constants.java b/src/main/java/ru/ulstu/fc/config/Constants.java new file mode 100644 index 0000000..7d922bb --- /dev/null +++ b/src/main/java/ru/ulstu/fc/config/Constants.java @@ -0,0 +1,12 @@ +package ru.ulstu.fc.config; + +public class Constants { + public static final int MIN_PASSWORD_LENGTH = 6; + + public static final String LOGIN_REGEX = "^[_'.@A-Za-z0-9-]*$"; + + public static final String COOKIES_NAME = "JSESSIONID"; + public static final String LOGOUT_URL = "/login?logout"; + public static final String SESSION_ID_ATTR = "sessionId"; + public static final int SESSION_TIMEOUT_SECONDS = 30 * 60; +} diff --git a/src/main/java/ru/ulstu/fc/config/GlobalDefaultExceptionHandler.java b/src/main/java/ru/ulstu/fc/config/GlobalDefaultExceptionHandler.java index 7e9d869..85ea92d 100644 --- a/src/main/java/ru/ulstu/fc/config/GlobalDefaultExceptionHandler.java +++ b/src/main/java/ru/ulstu/fc/config/GlobalDefaultExceptionHandler.java @@ -1,5 +1,6 @@ package ru.ulstu.fc.config; +import io.swagger.v3.oas.annotations.Hidden; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.http.HttpStatus; @@ -7,25 +8,13 @@ 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.NoHandlerFoundException; -import springfox.documentation.annotations.ApiIgnore; @ControllerAdvice -@ApiIgnore +@Hidden class GlobalDefaultExceptionHandler { private final static Logger LOG = LoggerFactory.getLogger(GlobalDefaultExceptionHandler.class); public static final String DEFAULT_ERROR_VIEW = "error"; - -// @ExceptionHandler(value = Exception.class) -// public ModelAndView defaultErrorHandler(HttpServletRequest req, Exception e) { -// LOG.warn(e.getMessage()); -// ModelAndView mav = new ModelAndView(); -// mav.addObject("exception", e); -// mav.addObject("url", req.getRequestURL()); -// mav.setViewName(DEFAULT_ERROR_VIEW); -// return mav; -// } - @ExceptionHandler(NoHandlerFoundException.class) @ResponseStatus(HttpStatus.NOT_FOUND) public String handle(NoHandlerFoundException ex) { diff --git a/src/main/java/ru/ulstu/fc/config/MvcConfiguration.java b/src/main/java/ru/ulstu/fc/config/MvcConfiguration.java index cf79a53..809147e 100644 --- a/src/main/java/ru/ulstu/fc/config/MvcConfiguration.java +++ b/src/main/java/ru/ulstu/fc/config/MvcConfiguration.java @@ -1,9 +1,3 @@ -/* - * Copyright (C) 2021 Anton Romanov - All Rights Reserved - * You may use, distribute and modify this code, please write to: romanov73@gmail.com. - * - */ - package ru.ulstu.fc.config; import org.springframework.context.annotation.Bean; @@ -11,19 +5,19 @@ import org.springframework.context.annotation.Configuration; import org.springframework.web.servlet.LocaleResolver; import org.springframework.web.servlet.config.annotation.InterceptorRegistry; import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry; +import org.springframework.web.servlet.config.annotation.ViewControllerRegistry; import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; import org.springframework.web.servlet.i18n.CookieLocaleResolver; import org.springframework.web.servlet.i18n.LocaleChangeInterceptor; -import springfox.documentation.builders.RequestHandlerSelectors; -import springfox.documentation.spi.DocumentationType; -import springfox.documentation.spring.web.plugins.Docket; @Configuration public class MvcConfiguration implements WebMvcConfigurer { -// @Override -// public void addViewControllers(ViewControllerRegistry registry) { -// registry.addViewController("/index"); -// } + @Override + public void addViewControllers(ViewControllerRegistry registry) { + registry.addViewController("/login"); + registry.addViewController("/loginError"); + registry.addViewController("/index"); + } @Override public void addResourceHandlers(ResourceHandlerRegistry registry) { @@ -44,14 +38,6 @@ public class MvcConfiguration implements WebMvcConfigurer { return localeInterceptor; } - @Bean - public Docket api() { - return new Docket(DocumentationType.SWAGGER_2) - .select() - .apis(RequestHandlerSelectors.basePackage("ru.ulstu")) - .build(); - } - @Override public void addInterceptors(InterceptorRegistry registry) { registry.addInterceptor(localeInterceptor()); diff --git a/src/main/java/ru/ulstu/fc/config/PasswordEncoderConfiguration.java b/src/main/java/ru/ulstu/fc/config/PasswordEncoderConfiguration.java new file mode 100644 index 0000000..4706fe8 --- /dev/null +++ b/src/main/java/ru/ulstu/fc/config/PasswordEncoderConfiguration.java @@ -0,0 +1,13 @@ +package ru.ulstu.fc.config; + +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; + +@Configuration +public class PasswordEncoderConfiguration { + @Bean + public BCryptPasswordEncoder bCryptPasswordEncoder() { + return new BCryptPasswordEncoder(); + } +} diff --git a/src/main/java/ru/ulstu/fc/config/SecurityConfiguration.java b/src/main/java/ru/ulstu/fc/config/SecurityConfiguration.java new file mode 100644 index 0000000..be6b4ea --- /dev/null +++ b/src/main/java/ru/ulstu/fc/config/SecurityConfiguration.java @@ -0,0 +1,51 @@ +package ru.ulstu.fc.config; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +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; +import org.springframework.security.web.SecurityFilterChain; +import ru.ulstu.fc.user.model.UserRoleConstants; + +@Configuration +@EnableWebSecurity +public class SecurityConfiguration { + private final Logger log = LoggerFactory.getLogger(SecurityConfiguration.class); + private final String[] permittedUrls = new String[]{ + "/login", "/index", "/user/register", + "/public/**", "/organizers", "/webjars/**", + "/error", "/register", + "/h2-console/*", "/h2-console", + "/css/**", "/js/**", "/img/**", + "/templates/**", "/webjars/**"}; + + @Bean + public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception { + log.debug("Security enabled"); + + http + .headers(headers -> headers.frameOptions(HeadersConfigurer.FrameOptionsConfig::disable)) + .csrf(AbstractHttpConfigurer::disable) + .authorizeHttpRequests(auth -> + auth.requestMatchers("/").permitAll() + .requestMatchers(permittedUrls).permitAll() + .requestMatchers("/swagger-ui.html").hasAuthority(UserRoleConstants.ADMIN) + .anyRequest().authenticated()) + .formLogin(form -> + form.loginPage("/login") + .failureUrl("/loginError") + .permitAll()) + .logout(logout -> + logout + .logoutSuccessUrl(Constants.LOGOUT_URL) + .invalidateHttpSession(false) + .clearAuthentication(true) + .deleteCookies(Constants.COOKIES_NAME) + .permitAll()); + return http.build(); + } +} \ No newline at end of file diff --git a/src/main/java/ru/ulstu/fc/config/TemplateConfiguration.java b/src/main/java/ru/ulstu/fc/config/TemplateConfiguration.java index 6bee3c7..8ecec34 100644 --- a/src/main/java/ru/ulstu/fc/config/TemplateConfiguration.java +++ b/src/main/java/ru/ulstu/fc/config/TemplateConfiguration.java @@ -1,19 +1,21 @@ package ru.ulstu.fc.config; -import nz.net.ultraq.thymeleaf.LayoutDialect; +import nz.net.ultraq.thymeleaf.layoutdialect.LayoutDialect; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; -import org.thymeleaf.spring5.SpringTemplateEngine; +import org.thymeleaf.extras.springsecurity6.dialect.SpringSecurityDialect; +import org.thymeleaf.spring6.SpringTemplateEngine; import org.thymeleaf.templateresolver.ITemplateResolver; @Configuration public class TemplateConfiguration { @Bean - public SpringTemplateEngine templateEngine(ITemplateResolver templateResolver) { + public SpringTemplateEngine templateEngine(ITemplateResolver templateResolver, SpringSecurityDialect sec) { final SpringTemplateEngine templateEngine = new SpringTemplateEngine(); templateEngine.addTemplateResolver(templateResolver); templateEngine.addDialect(new LayoutDialect()); + templateEngine.addDialect(sec); return templateEngine; } } diff --git a/src/main/java/ru/ulstu/fc/config/WebClientConfiguration.java b/src/main/java/ru/ulstu/fc/config/WebClientConfiguration.java deleted file mode 100644 index 1761fc4..0000000 --- a/src/main/java/ru/ulstu/fc/config/WebClientConfiguration.java +++ /dev/null @@ -1,13 +0,0 @@ -package ru.ulstu.fc.config; - -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; -import org.springframework.web.reactive.function.client.WebClient; - -@Configuration -public class WebClientConfiguration { - @Bean - public WebClient webClient(WebClient.Builder webClientBuilder) { - return webClientBuilder.build(); - } -} diff --git a/src/main/java/ru/ulstu/fc/core/model/BaseEntity.java b/src/main/java/ru/ulstu/fc/core/model/BaseEntity.java new file mode 100644 index 0000000..265bc4f --- /dev/null +++ b/src/main/java/ru/ulstu/fc/core/model/BaseEntity.java @@ -0,0 +1,84 @@ +package ru.ulstu.fc.core.model; + +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.GenerationType; +import jakarta.persistence.Id; +import jakarta.persistence.MappedSuperclass; +import jakarta.persistence.Version; +import jakarta.validation.constraints.NotNull; + +import java.io.Serializable; + +@MappedSuperclass +public abstract class BaseEntity implements Serializable, Comparable { + @Id + @GeneratedValue(strategy = GenerationType.SEQUENCE) + private Integer id; + + @Version + private Integer version; + + public Integer getId() { + return id; + } + + public void setId(Integer id) { + this.id = id; + } + + public Integer getVersion() { + return version; + } + + public void setVersion(Integer version) { + this.version = version; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj == null) { + return false; + } + if (!getClass().isAssignableFrom(obj.getClass())) { + return false; + } + BaseEntity other = (BaseEntity) obj; + if (id == null) { + if (other.id != null) { + return false; + } + } else if (!id.equals(other.id)) { + return false; + } + return true; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + (id == null ? 0 : id.hashCode()); + return result; + } + + @Override + public String toString() { + return getClass().getSimpleName() + "{" + + "id=" + id + + ", version=" + version + + '}'; + } + + @Override + public int compareTo(@NotNull BaseEntity o) { + return id != null ? id.compareTo(o.getId()) : -1; + } + + public void reset() { + this.id = null; + this.version = null; + } +} \ No newline at end of file diff --git a/src/main/java/ru/ulstu/fc/core/model/OffsetablePageRequest.java b/src/main/java/ru/ulstu/fc/core/model/OffsetablePageRequest.java new file mode 100644 index 0000000..abd3c06 --- /dev/null +++ b/src/main/java/ru/ulstu/fc/core/model/OffsetablePageRequest.java @@ -0,0 +1,119 @@ +package ru.ulstu.fc.core.model; + +import org.springframework.data.domain.Pageable; +import org.springframework.data.domain.Sort; + +import java.io.Serializable; +import java.util.Optional; + +public class OffsetablePageRequest implements Pageable, Serializable { + private final int offset; + private final int count; + private final Sort sort; + + public OffsetablePageRequest(int page, long pageSize) { + this(pageSize * page, pageSize, Sort.by("id")); + } + + public OffsetablePageRequest(long offset, long count, Sort sort) { + if (offset < 0) { + throw new IllegalArgumentException("Offset value must not be less than zero!"); + } + if (count < 1) { + throw new IllegalArgumentException("Count value must not be less than one!"); + } + this.offset = (int) offset; + this.count = (int) count; + this.sort = sort; + } + + @Override + public Sort getSort() { + return sort; + } + + @Override + public Sort getSortOr(Sort sort) { + return Pageable.super.getSortOr(sort); + } + + @Override + public int getPageSize() { + return count; + } + + @Override + public boolean isPaged() { + return Pageable.super.isPaged(); + } + + @Override + public boolean isUnpaged() { + return Pageable.super.isUnpaged(); + } + + @Override + public int getPageNumber() { + return offset / count; + } + + @Override + public long getOffset() { + return offset; + } + + @Override + public boolean hasPrevious() { + return offset > 0; + } + + @Override + public Optional toOptional() { + return Pageable.super.toOptional(); + } + + @Override + public Pageable next() { + return new OffsetablePageRequest(getOffset() + getPageSize(), getPageSize(), getSort()); + } + + @Override + public Pageable previousOrFirst() { + return hasPrevious() ? previous() : first(); + } + + public Pageable previous() { + return getOffset() == 0 ? this : new OffsetablePageRequest(getOffset() - getPageSize(), getPageSize(), getSort()); + } + + @Override + public Pageable first() { + return new OffsetablePageRequest(0, getPageSize(), getSort()); + } + + @Override + public Pageable withPage(int pageNumber) { + return null; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj == null || getClass() != obj.getClass()) { + return false; + } + final OffsetablePageRequest other = (OffsetablePageRequest) obj; + return this.offset == other.offset && this.count == other.count; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + offset; + result = prime * result + count; + return result; + } +} diff --git a/src/main/java/ru/ulstu/fc/project/controller/ProjectController.java b/src/main/java/ru/ulstu/fc/project/controller/ProjectController.java new file mode 100644 index 0000000..ca23828 --- /dev/null +++ b/src/main/java/ru/ulstu/fc/project/controller/ProjectController.java @@ -0,0 +1,66 @@ +package ru.ulstu.fc.project.controller; + +import io.swagger.v3.oas.annotations.Hidden; +import org.springframework.security.access.annotation.Secured; +import org.springframework.stereotype.Controller; +import org.springframework.ui.Model; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import ru.ulstu.fc.project.model.Project; +import ru.ulstu.fc.project.model.ProjectForm; +import ru.ulstu.fc.project.service.ProjectRulesService; +import ru.ulstu.fc.project.service.ProjectService; +import ru.ulstu.fc.project.service.ProjectVariableService; +import ru.ulstu.fc.user.model.UserRoleConstants; + +@Controller +@Hidden +@RequestMapping("project") +@Secured({UserRoleConstants.ADMIN}) +public class ProjectController { + private final ProjectService projectService; + private final ProjectRulesService projectRulesService; + private final ProjectVariableService projectVariableService; + + public ProjectController(ProjectService projectService, + ProjectRulesService projectRulesService, + ProjectVariableService projectVariableService) { + this.projectService = projectService; + this.projectRulesService = projectRulesService; + this.projectVariableService = projectVariableService; + } + + @GetMapping("list") + public String getProjects(Model model) { + model.addAttribute("projects", projectService.getCurrentUserProjects()); + return "project/list"; + } + + @GetMapping("/edit/{projectId}") + public String edit(@PathVariable(value = "projectId") Integer id, Model model) { + model.addAttribute("project", + new ProjectForm((id != null && id != 0) + ? projectService.getById(id) + : new Project())); + + model.addAttribute("rules", projectRulesService.getByProjectId(id)); + model.addAttribute("variables", projectVariableService.getByProjectId(id)); + return "project/edit"; + } + + @PostMapping(value = "save", params = "save") + public String save(ProjectForm projectForm, Model model) { + model.addAttribute("project", projectService.save(projectForm)); + return "redirect:/project/list"; + } + + @PostMapping(value = "save", params = "delete") + public String delete(ProjectForm projectForm) { + if (projectForm != null && projectForm.getId() != null) { + projectService.delete(projectForm); + } + return "redirect:/project/list"; + } +} diff --git a/src/main/java/ru/ulstu/fc/project/controller/ProjectRestController.java b/src/main/java/ru/ulstu/fc/project/controller/ProjectRestController.java new file mode 100644 index 0000000..7fcce20 --- /dev/null +++ b/src/main/java/ru/ulstu/fc/project/controller/ProjectRestController.java @@ -0,0 +1,41 @@ +package ru.ulstu.fc.project.controller; + +import org.springframework.security.access.annotation.Secured; +import org.springframework.web.bind.annotation.DeleteMapping; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; +import ru.ulstu.fc.project.model.Project; +import ru.ulstu.fc.project.model.ProjectForm; +import ru.ulstu.fc.project.service.ProjectService; +import ru.ulstu.fc.user.model.UserRoleConstants; + +import java.util.List; + +@RestController +@RequestMapping("projectRest") +@Secured({UserRoleConstants.ADMIN}) +public class ProjectRestController { + private final ProjectService projectService; + + public ProjectRestController(ProjectService projectService) { + this.projectService = projectService; + } + + @GetMapping("list") + public List getProjects() { + return projectService.getCurrentUserProjects(); + } + + @PostMapping("save") + public Project save(Project project) { + return projectService.save(project); + } + + @DeleteMapping("delete") + public String delete(ProjectForm projectForm) { + projectService.delete(projectForm); + return "redirect:/list"; + } +} diff --git a/src/main/java/ru/ulstu/fc/project/model/Project.java b/src/main/java/ru/ulstu/fc/project/model/Project.java new file mode 100644 index 0000000..df655b0 --- /dev/null +++ b/src/main/java/ru/ulstu/fc/project/model/Project.java @@ -0,0 +1,53 @@ +package ru.ulstu.fc.project.model; + +import jakarta.persistence.CascadeType; +import jakarta.persistence.Entity; +import jakarta.persistence.ManyToOne; +import jakarta.validation.constraints.NotEmpty; +import ru.ulstu.fc.core.model.BaseEntity; +import ru.ulstu.fc.user.model.User; + +import java.util.Date; + +@Entity +public class Project extends BaseEntity { + @NotEmpty(message = "Текст новости не может быть пустым") + private String name; + private Date createDate = new Date(); + @ManyToOne(cascade = CascadeType.MERGE) + private User user; + + public Project() { + } + + public Project(ProjectForm projectForm) { + if (projectForm.getId() != null) { + setId(projectForm.getId()); + } + this.name = projectForm.getName(); + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public Date getCreateDate() { + return createDate; + } + + public void setCreateDate(Date createDate) { + this.createDate = createDate; + } + + public User getUser() { + return user; + } + + public void setUser(User user) { + this.user = user; + } +} diff --git a/src/main/java/ru/ulstu/fc/project/model/ProjectForm.java b/src/main/java/ru/ulstu/fc/project/model/ProjectForm.java new file mode 100644 index 0000000..a03532b --- /dev/null +++ b/src/main/java/ru/ulstu/fc/project/model/ProjectForm.java @@ -0,0 +1,38 @@ +package ru.ulstu.fc.project.model; + +import java.util.Date; + +public class ProjectForm { + private Integer id; + private String name; + private Date createDate; + + public ProjectForm() { + } + + public ProjectForm(Project project) { + this.id = project.getId(); + this.name = project.getName(); + this.createDate = project.getCreateDate(); + } + + public Integer getId() { + return id; + } + + public void setId(Integer id) { + this.id = id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public Date getCreateDate() { + return createDate; + } +} diff --git a/src/main/java/ru/ulstu/fc/project/repository/ProjectRepository.java b/src/main/java/ru/ulstu/fc/project/repository/ProjectRepository.java new file mode 100644 index 0000000..547ac15 --- /dev/null +++ b/src/main/java/ru/ulstu/fc/project/repository/ProjectRepository.java @@ -0,0 +1,14 @@ +package ru.ulstu.fc.project.repository; + +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.repository.query.Param; +import org.springframework.stereotype.Repository; +import ru.ulstu.fc.project.model.Project; + +import java.util.List; + +@Repository +public interface ProjectRepository extends JpaRepository { + + List findAllByUserId(@Param("userId") Integer userId); +} diff --git a/src/main/java/ru/ulstu/fc/project/service/ProjectRulesService.java b/src/main/java/ru/ulstu/fc/project/service/ProjectRulesService.java new file mode 100644 index 0000000..52f1d72 --- /dev/null +++ b/src/main/java/ru/ulstu/fc/project/service/ProjectRulesService.java @@ -0,0 +1,27 @@ +package ru.ulstu.fc.project.service; + +import org.springframework.stereotype.Service; +import ru.ulstu.fc.rule.model.FuzzyRule; +import ru.ulstu.fc.rule.repository.FuzzyRuleRepository; + +import java.util.Collections; +import java.util.List; + +@Service +public class ProjectRulesService { + private final FuzzyRuleRepository ruleRepository; + private final ProjectService projectService; + + public ProjectRulesService(FuzzyRuleRepository ruleRepository, + ProjectService projectService) { + this.ruleRepository = ruleRepository; + this.projectService = projectService; + } + + public List getByProjectId(Integer projectId) { + if (projectId == null || projectId == 0) { + return Collections.emptyList(); + } + return ruleRepository.findByProject(projectService.getById(projectId)); + } +} diff --git a/src/main/java/ru/ulstu/fc/project/service/ProjectService.java b/src/main/java/ru/ulstu/fc/project/service/ProjectService.java new file mode 100644 index 0000000..39a9cb5 --- /dev/null +++ b/src/main/java/ru/ulstu/fc/project/service/ProjectService.java @@ -0,0 +1,64 @@ +package ru.ulstu.fc.project.service; + +import org.springframework.stereotype.Service; +import ru.ulstu.fc.project.model.Project; +import ru.ulstu.fc.project.model.ProjectForm; +import ru.ulstu.fc.project.repository.ProjectRepository; +import ru.ulstu.fc.user.model.User; +import ru.ulstu.fc.user.service.UserService; + +import java.util.List; + +@Service +public class ProjectService { + private final ProjectRepository projectRepository; + private final UserService userService; + + public ProjectService(ProjectRepository projectRepository, + UserService userService) { + this.projectRepository = projectRepository; + this.userService = userService; + } + + public List getCurrentUserProjects() { + return projectRepository.findAllByUserId(userService.getCurrentUser().getId()); + } + + public Project getById(Integer id) { + Project project = projectRepository + .findById(id) + .orElseThrow(() -> new RuntimeException("Project not found by id")); + checkUserProjectWithThrow(project, userService.getCurrentUser()); + return project; + } + + public Project save(ProjectForm projectForm) { + return save(new Project(projectForm)); + } + + public Project save(Project projectToSave) { + User currentUser = userService.getCurrentUser(); + if (projectToSave.getId() == null) { + projectToSave.setUser(currentUser); + return projectRepository.save(projectToSave); + } + Project dbProject = getById(projectToSave.getId()); + dbProject.setName(projectToSave.getName()); + return projectRepository.save(dbProject); + } + + public void delete(ProjectForm projectForm) { + getById(projectForm.getId()); + projectRepository.deleteById(projectForm.getId()); + } + + private void checkUserProjectWithThrow(Project project, User currentUser) { + if (!isUserProject(project, currentUser)) { + throw new RuntimeException("User can not get access to project"); + } + } + + private boolean isUserProject(Project project, User currentUser) { + return (currentUser.equals(project.getUser())); + } +} diff --git a/src/main/java/ru/ulstu/fc/project/service/ProjectVariableService.java b/src/main/java/ru/ulstu/fc/project/service/ProjectVariableService.java new file mode 100644 index 0000000..5ec7194 --- /dev/null +++ b/src/main/java/ru/ulstu/fc/project/service/ProjectVariableService.java @@ -0,0 +1,27 @@ +package ru.ulstu.fc.project.service; + +import org.springframework.stereotype.Service; +import ru.ulstu.fc.rule.model.Variable; +import ru.ulstu.fc.rule.repository.VariableRepository; + +import java.util.Collections; +import java.util.List; + +@Service +public class ProjectVariableService { + private final VariableRepository variableRepository; + private final ProjectService projectService; + + public ProjectVariableService(VariableRepository variableRepository, + ProjectService projectService) { + this.variableRepository = variableRepository; + this.projectService = projectService; + } + + public List getByProjectId(Integer projectId) { + if (projectId == null || projectId == 0) { + return Collections.emptyList(); + } + return variableRepository.findByProject(projectService.getById(projectId)); + } +} diff --git a/src/main/java/ru/ulstu/fc/rule/controller/FuzzyTermController.java b/src/main/java/ru/ulstu/fc/rule/controller/FuzzyTermController.java new file mode 100644 index 0000000..895a412 --- /dev/null +++ b/src/main/java/ru/ulstu/fc/rule/controller/FuzzyTermController.java @@ -0,0 +1,54 @@ +package ru.ulstu.fc.rule.controller; + +import jakarta.validation.Valid; +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.PathVariable; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import ru.ulstu.fc.rule.model.FuzzyTermForm; +import ru.ulstu.fc.rule.service.FuzzyTermService; + +@Controller +@RequestMapping("fuzzyTerm") +public class FuzzyTermController { + private final FuzzyTermService fuzzyTermService; + + public FuzzyTermController(FuzzyTermService fuzzyTermService) { + this.fuzzyTermService = fuzzyTermService; + } + + @GetMapping("/edit/{projectId}/{variableId}/{fuzzyTermId}") + public String edit(@PathVariable(value = "projectId") Integer projectId, + @PathVariable(value = "variableId") Integer variableId, + @PathVariable(value = "fuzzyTermId") Integer fuzzyTermId, Model model) { + model.addAttribute("projectId", projectId); + model.addAttribute("variableId", variableId); + model.addAttribute("fuzzyTermForm", + (fuzzyTermId == null || fuzzyTermId == 0) + ? new FuzzyTermForm(fuzzyTermId, projectId, variableId) + : new FuzzyTermForm(fuzzyTermId, projectId, variableId, fuzzyTermService.getById(fuzzyTermId))); + model.addAttribute("fuzzyTerms", fuzzyTermService.getByVariableId(variableId)); + return "fuzzyTerm/edit"; + } + + @PostMapping(value = "save", params = "save") + public String save(@Valid FuzzyTermForm fuzzyTermForm, BindingResult result, Model model) { + if (result.hasErrors()) { + model.addAttribute("projectId", fuzzyTermForm.getProjectId()); + return "fuzzyTerm/edit"; + } + fuzzyTermService.save(fuzzyTermForm); + return "redirect:/variable/edit/" + fuzzyTermForm.getProjectId() + "/" + fuzzyTermForm.getVariableId(); + } + + @PostMapping(value = "save", params = "delete") + public String delete(FuzzyTermForm fuzzyTermForm) { + if (fuzzyTermForm != null && fuzzyTermForm.getId() != null) { + fuzzyTermService.delete(fuzzyTermForm); + } + return "redirect:/variable/edit/" + fuzzyTermForm.getProjectId() + "/" + fuzzyTermForm.getVariableId(); + } +} diff --git a/src/main/java/ru/ulstu/fc/rule/controller/InferenceMvcController.java b/src/main/java/ru/ulstu/fc/rule/controller/InferenceMvcController.java index b18e7af..67bebab 100644 --- a/src/main/java/ru/ulstu/fc/rule/controller/InferenceMvcController.java +++ b/src/main/java/ru/ulstu/fc/rule/controller/InferenceMvcController.java @@ -1,22 +1,23 @@ package ru.ulstu.fc.rule.controller; +import io.swagger.v3.oas.annotations.Hidden; import org.springframework.stereotype.Controller; import org.springframework.ui.Model; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.ModelAttribute; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; -import ru.ulstu.fc.rule.model.Antecedent; +import ru.ulstu.fc.rule.model.FuzzyTerm; import ru.ulstu.fc.rule.model.InferenceForm; +import ru.ulstu.fc.rule.model.Variable; import ru.ulstu.fc.rule.service.FuzzyInferenceService; -import springfox.documentation.annotations.ApiIgnore; import java.util.Arrays; import java.util.List; import java.util.Map; @Controller -@ApiIgnore +@Hidden public class InferenceMvcController { private final FuzzyInferenceService fuzzyInferenceService; @@ -26,35 +27,38 @@ public class InferenceMvcController { @GetMapping("/") public String initInference(Model model) { - model.addAttribute("ageAntecedents", getAgeAntecedents()); - model.addAttribute("incomeAntecedents", getIncomeAntecedents()); + model.addAttribute("ageValues", getAgeValues()); + model.addAttribute("incomeValues", getIncomeValues()); model.addAttribute("inferenceForm", new InferenceForm()); return "index"; } @RequestMapping(value = "get-inference", method = RequestMethod.POST) public String getInference(@ModelAttribute InferenceForm inferenceForm, Model model) { - model.addAttribute("ageAntecedents", getAgeAntecedents()); - model.addAttribute("incomeAntecedents", getIncomeAntecedents()); + model.addAttribute("ageValues", getAgeValues()); + model.addAttribute("incomeValues", getIncomeValues()); model.addAttribute("inferenceForm", inferenceForm); model.addAttribute("response", fuzzyInferenceService.getFuzzyInference( - Map.of("возраст", Double.valueOf(inferenceForm.getAgeAntecedent()), - "доход", Double.valueOf(inferenceForm.getIncomeAntecedent()) - ))); + Map.of("возраст", Double.valueOf(inferenceForm.getAgeValue()), + "доход", Double.valueOf(inferenceForm.getIncomeValue())))); return "index"; } - private List getAgeAntecedents() { - return Arrays.asList( - new Antecedent("молодой", "30"), - new Antecedent("средний", "45"), - new Antecedent("старый", "60")); + private List getAgeValues() { + Variable var = new Variable("Age"); + var.getFuzzyTerms().addAll(Arrays.asList( + new FuzzyTerm("молодой", 30.0), + new FuzzyTerm("средний", 45.0), + new FuzzyTerm("старый", 60.0))); + return var.getFuzzyTerms(); } - private List getIncomeAntecedents() { - return Arrays.asList( - new Antecedent("небольшой", "20000"), - new Antecedent("средний", "90000"), - new Antecedent("высокий", "200000")); + private List getIncomeValues() { + Variable var = new Variable("Income"); + var.getFuzzyTerms().addAll(Arrays.asList( + new FuzzyTerm("небольшой", 20000.0), + new FuzzyTerm("средний", 90000.0), + new FuzzyTerm("высокий", 200000.0))); + return var.getFuzzyTerms(); } } diff --git a/src/main/java/ru/ulstu/fc/rule/controller/InferenceRestController.java b/src/main/java/ru/ulstu/fc/rule/controller/InferenceRestController.java index 0f3fb7e..dcb0a8f 100644 --- a/src/main/java/ru/ulstu/fc/rule/controller/InferenceRestController.java +++ b/src/main/java/ru/ulstu/fc/rule/controller/InferenceRestController.java @@ -11,7 +11,7 @@ import ru.ulstu.fc.rule.service.FuzzyInferenceService; import java.util.List; @RestController -@RequestMapping("rest") +@RequestMapping("inferenceRest") public class InferenceRestController { private final FuzzyInferenceService fuzzyInferenceService; diff --git a/src/main/java/ru/ulstu/fc/rule/controller/RuleController.java b/src/main/java/ru/ulstu/fc/rule/controller/RuleController.java index 628a315..a7940ad 100644 --- a/src/main/java/ru/ulstu/fc/rule/controller/RuleController.java +++ b/src/main/java/ru/ulstu/fc/rule/controller/RuleController.java @@ -1,96 +1,51 @@ package ru.ulstu.fc.rule.controller; +import jakarta.validation.Valid; 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.RequestParam; -import ru.ulstu.fc.rule.model.AddRuleForm; -import ru.ulstu.fc.rule.model.AddTermForm; -import ru.ulstu.fc.rule.model.AddVariableForm; -import ru.ulstu.fc.rule.service.RuleParseService; -import ru.ulstu.fc.rule.service.RuleService; -import ru.ulstu.fc.rule.service.TermsService; -import ru.ulstu.fc.rule.service.VariableService; - -import java.util.List; +import org.springframework.web.bind.annotation.RequestMapping; +import ru.ulstu.fc.rule.model.FuzzyRuleForm; +import ru.ulstu.fc.rule.service.FuzzyRuleService; @Controller +@RequestMapping("rule") public class RuleController { - private final RuleParseService ruleParseService; - private final RuleService ruleService; - private final TermsService termService; - private final VariableService variableService; + private final FuzzyRuleService ruleService; - public RuleController(RuleParseService ruleParseService, - RuleService ruleService, - TermsService termService, - VariableService variableService) { - this.ruleParseService = ruleParseService; + public RuleController(FuzzyRuleService ruleService) { this.ruleService = ruleService; - this.termService = termService; - this.variableService = variableService; } - @GetMapping("listRules") - public String listRules(Model model) { - model.addAttribute("rules", ruleService.getRules()); - return "listRules"; + @GetMapping("/edit/{projectId}/{ruleId}") + public String edit(@PathVariable(value = "projectId") Integer projectId, + @PathVariable(value = "ruleId") Integer id, Model model) { + model.addAttribute("projectId", projectId); + model.addAttribute("fuzzyRuleForm", + (id != null && id != 0) + ? new FuzzyRuleForm(id, ruleService.getById(id)) + : new FuzzyRuleForm(id, projectId)); + return "rule/edit"; } - @GetMapping("listTerms") - public String listTerms(Model model) { - model.addAttribute("terms", termService.getTerms()); - return "listTerms"; - } - - @GetMapping("listVars") - public String listVariables(Model model) { - model.addAttribute("vars", variableService.getVars()); - return "listVars"; - } - - @GetMapping("addRule") - public String addRule(Model model) { - model.addAttribute("addRuleForm", new AddRuleForm()); - return "addRule"; - } - - @PostMapping("addRule") - public String parse(@ModelAttribute AddRuleForm addRuleForm, Model model) { - try { - System.out.println(ruleParseService.parseRules(List.of(addRuleForm.getRule()))); - } catch (Exception ex) { - return "addRule"; + @PostMapping(value = "save", params = "save") + public String save(@Valid FuzzyRuleForm fuzzyRuleForm, BindingResult bindingResult, Model model) { + if (bindingResult.hasErrors()) { + model.addAttribute("projectId", fuzzyRuleForm.getProjectId()); + return "rule/edit"; } - model.addAttribute("addRuleForm", addRuleForm); - return "listRules"; + ruleService.save(fuzzyRuleForm); + return "redirect:/project/edit/" + fuzzyRuleForm.getProjectId(); } - @GetMapping("addVariable") - public String addVariable(Model model, @RequestParam(required = false) Integer id) { - model.addAttribute("addVariableForm", variableService.getAddVariableFormOrDefault(id)); - return "addVariable"; - } - - @PostMapping("addVariable") - public String addVariable(@ModelAttribute AddVariableForm addVariableForm, Model model) { - model.addAttribute("addVariableForm", addVariableForm); - variableService.save(addVariableForm); - model.addAttribute("vars", variableService.getVars()); - return "listVars"; - } - - @GetMapping("addTerm") - public String addTerm(Model model) { - model.addAttribute("addTermForm", new AddTermForm()); - return "addTerm"; - } - - @PostMapping("addTerm") - public String addTerm(@ModelAttribute AddTermForm addTermForm, Model model) { - model.addAttribute("addTermForm", addTermForm); - return "listTerms"; + @PostMapping(value = "save", params = "delete") + public String delete(FuzzyRuleForm fuzzyRuleForm) { + if (fuzzyRuleForm != null && fuzzyRuleForm.getId() != null) { + ruleService.delete(fuzzyRuleForm); + } + return "redirect:/project/edit/" + fuzzyRuleForm.getProjectId(); } } diff --git a/src/main/java/ru/ulstu/fc/rule/controller/VariableController.java b/src/main/java/ru/ulstu/fc/rule/controller/VariableController.java new file mode 100644 index 0000000..4b21a54 --- /dev/null +++ b/src/main/java/ru/ulstu/fc/rule/controller/VariableController.java @@ -0,0 +1,56 @@ +package ru.ulstu.fc.rule.controller; + +import jakarta.validation.Valid; +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.PathVariable; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import ru.ulstu.fc.rule.model.VariableForm; +import ru.ulstu.fc.rule.service.FuzzyTermService; +import ru.ulstu.fc.rule.service.VariableService; + +@Controller +@RequestMapping("variable") +public class VariableController { + private final VariableService variableService; + private final FuzzyTermService termService; + + public VariableController(VariableService variableService, + FuzzyTermService termService) { + this.variableService = variableService; + this.termService = termService; + } + + @GetMapping("/edit/{projectId}/{variableId}") + public String edit(@PathVariable(value = "projectId") Integer projectId, + @PathVariable(value = "variableId") Integer variableId, Model model) { + model.addAttribute("projectId", projectId); + model.addAttribute("variableForm", + (variableId == null || variableId == 0) + ? new VariableForm(variableId, projectId) + : new VariableForm(variableId, variableService.getById(variableId))); + model.addAttribute("fuzzyTerms", termService.getByVariableId(variableId)); + return "variable/edit"; + } + + @PostMapping(value = "save", params = "save") + public String save(@Valid VariableForm variableForm, BindingResult result, Model model) { + if (result.hasErrors()) { + model.addAttribute("projectId", variableForm.getProjectId()); + return "variable/edit"; + } + variableService.save(variableForm); + return "redirect:/project/edit/" + variableForm.getProjectId(); + } + + @PostMapping(value = "save", params = "delete") + public String delete(VariableForm variableForm) { + if (variableForm != null && variableForm.getId() != null) { + variableService.delete(variableForm); + } + return "redirect:/project/edit/" + variableForm.getProjectId(); + } +} diff --git a/src/main/java/ru/ulstu/fc/rule/model/Antecedent.java b/src/main/java/ru/ulstu/fc/rule/model/Antecedent.java deleted file mode 100644 index 54915e1..0000000 --- a/src/main/java/ru/ulstu/fc/rule/model/Antecedent.java +++ /dev/null @@ -1,19 +0,0 @@ -package ru.ulstu.fc.rule.model; - -public class Antecedent { - private String value; - private String description; - - public Antecedent(String description, String value) { - this.description = description; - this.value = value; - } - - public String getValue() { - return value; - } - - public String getDescription() { - return description; - } -} diff --git a/src/main/java/ru/ulstu/fc/rule/model/FuzzyRule.java b/src/main/java/ru/ulstu/fc/rule/model/FuzzyRule.java new file mode 100644 index 0000000..04b07ec --- /dev/null +++ b/src/main/java/ru/ulstu/fc/rule/model/FuzzyRule.java @@ -0,0 +1,36 @@ +package ru.ulstu.fc.rule.model; + +import jakarta.persistence.Entity; +import jakarta.persistence.ManyToOne; +import jakarta.validation.constraints.NotNull; +import jakarta.validation.constraints.Size; +import ru.ulstu.fc.core.model.BaseEntity; +import ru.ulstu.fc.project.model.Project; + +@Entity +public class FuzzyRule extends BaseEntity { + @ManyToOne + @NotNull + private Project project; + @Size(min = 5, max = 250, message = "Длина от 5 до 250 символов") + private String content; + + public FuzzyRule() { + } + + public Project getProject() { + return project; + } + + public void setProject(Project project) { + this.project = project; + } + + public String getContent() { + return content; + } + + public void setContent(String content) { + this.content = content; + } +} diff --git a/src/main/java/ru/ulstu/fc/rule/model/FuzzyRuleForm.java b/src/main/java/ru/ulstu/fc/rule/model/FuzzyRuleForm.java new file mode 100644 index 0000000..5ae6d32 --- /dev/null +++ b/src/main/java/ru/ulstu/fc/rule/model/FuzzyRuleForm.java @@ -0,0 +1,52 @@ +package ru.ulstu.fc.rule.model; + +import jakarta.validation.constraints.NotNull; +import jakarta.validation.constraints.Size; + +public class FuzzyRuleForm { + private Integer id; + @NotNull + private Integer projectId; + + @Size(min = 5, max = 250, message = "Длина от 5 до 250 символов") + private String content; + + public FuzzyRuleForm() { + } + + public FuzzyRuleForm(Integer id, Integer projectId) { + this.id = id; + this.projectId = projectId; + } + + public FuzzyRuleForm(Integer id, FuzzyRule fuzzyRule) { + this.id = fuzzyRule.getId(); + this.projectId = fuzzyRule.getProject().getId(); + this.content = fuzzyRule.getContent(); + } + + public Integer getProjectId() { + return projectId; + } + + public Integer getId() { + return id; + } + + public void setId(Integer id) { + this.id = id; + } + + public void setProjectId(Integer projectId) { + this.projectId = projectId; + } + + public String getContent() { + return content; + } + + public void setContent(String content) { + this.content = content; + } + +} diff --git a/src/main/java/ru/ulstu/fc/rule/model/FuzzyTerm.java b/src/main/java/ru/ulstu/fc/rule/model/FuzzyTerm.java new file mode 100644 index 0000000..7e8d278 --- /dev/null +++ b/src/main/java/ru/ulstu/fc/rule/model/FuzzyTerm.java @@ -0,0 +1,34 @@ +package ru.ulstu.fc.rule.model; + +import jakarta.persistence.Entity; +import ru.ulstu.fc.core.model.BaseEntity; + +@Entity +public class FuzzyTerm extends BaseEntity { + private String description; + private Double crispValue; + + public FuzzyTerm() { + } + + public FuzzyTerm(String description, Double value) { + this.description = description; + this.crispValue = value; + } + + public String getDescription() { + return description; + } + + public void setDescription(String description) { + this.description = description; + } + + public Double getCrispValue() { + return crispValue; + } + + public void setCrispValue(Double crispValue) { + this.crispValue = crispValue; + } +} diff --git a/src/main/java/ru/ulstu/fc/rule/model/FuzzyTermForm.java b/src/main/java/ru/ulstu/fc/rule/model/FuzzyTermForm.java new file mode 100644 index 0000000..755fda3 --- /dev/null +++ b/src/main/java/ru/ulstu/fc/rule/model/FuzzyTermForm.java @@ -0,0 +1,71 @@ +package ru.ulstu.fc.rule.model; + +public class FuzzyTermForm { + private Integer projectId; + private Integer variableId; + private Integer id; + private String description; + private Double crispValue; + + public FuzzyTermForm() { + } + + public FuzzyTermForm(String description, Double value) { + this.description = description; + this.crispValue = value; + } + + public FuzzyTermForm(Integer id, Integer projectId, Integer variableId) { + this.projectId = projectId; + this.variableId = variableId; + this.id = id; + } + + public FuzzyTermForm(Integer id, Integer projectId, Integer variableId, FuzzyTerm fuzzyTerm) { + this.id = id; + this.projectId = projectId; + this.variableId = variableId; + this.crispValue = fuzzyTerm.getCrispValue(); + this.description = fuzzyTerm.getDescription(); + } + + public String getDescription() { + return description; + } + + public void setDescription(String description) { + this.description = description; + } + + public Double getCrispValue() { + return crispValue; + } + + public void setCrispValue(Double crispValue) { + this.crispValue = crispValue; + } + + public Integer getId() { + return id; + } + + public void setId(Integer id) { + this.id = id; + } + + public Integer getProjectId() { + return projectId; + } + + public void setProjectId(Integer projectId) { + this.projectId = projectId; + } + + public Integer getVariableId() { + return variableId; + } + + public void setVariableId(Integer variableId) { + this.variableId = variableId; + } +} diff --git a/src/main/java/ru/ulstu/fc/rule/model/InferenceForm.java b/src/main/java/ru/ulstu/fc/rule/model/InferenceForm.java index d336986..7a8ac7e 100644 --- a/src/main/java/ru/ulstu/fc/rule/model/InferenceForm.java +++ b/src/main/java/ru/ulstu/fc/rule/model/InferenceForm.java @@ -1,22 +1,22 @@ package ru.ulstu.fc.rule.model; public class InferenceForm { - private String ageAntecedent; - private String incomeAntecedent; + private String ageValue; + private String incomeValue; - public String getAgeAntecedent() { - return ageAntecedent; + public String getAgeValue() { + return ageValue; } - public void setAgeAntecedent(String ageAntecedent) { - this.ageAntecedent = ageAntecedent; + public void setAgeValue(String ageAntecedent) { + this.ageValue = ageAntecedent; } - public String getIncomeAntecedent() { - return incomeAntecedent; + public String getIncomeValue() { + return incomeValue; } - public void setIncomeAntecedent(String incomeAntecedent) { - this.incomeAntecedent = incomeAntecedent; + public void setIncomeValue(String incomeAntecedent) { + this.incomeValue = incomeAntecedent; } } diff --git a/src/main/java/ru/ulstu/fc/rule/model/Variable.java b/src/main/java/ru/ulstu/fc/rule/model/Variable.java index 265e189..9970fa7 100644 --- a/src/main/java/ru/ulstu/fc/rule/model/Variable.java +++ b/src/main/java/ru/ulstu/fc/rule/model/Variable.java @@ -1,16 +1,33 @@ package ru.ulstu.fc.rule.model; -import ru.ulstu.fc.core.BaseEntity; +import jakarta.persistence.CascadeType; +import jakarta.persistence.Entity; +import jakarta.persistence.FetchType; +import jakarta.persistence.JoinColumn; +import jakarta.persistence.ManyToOne; +import jakarta.persistence.OneToMany; +import jakarta.validation.constraints.NotNull; +import jakarta.validation.constraints.Size; +import ru.ulstu.fc.core.model.BaseEntity; +import ru.ulstu.fc.project.model.Project; -import javax.persistence.Entity; -import javax.persistence.OneToMany; +import java.util.ArrayList; import java.util.List; @Entity public class Variable extends BaseEntity { + @Size(min = 3, max = 250, message = "Длина должна быть от 3 до 250") private String name; - @OneToMany - private List terms; + + @ManyToOne + @NotNull + private Project project; + + private boolean input = true; + + @OneToMany(cascade = CascadeType.ALL, fetch = FetchType.LAZY) + @JoinColumn(name = "variable_id") + private List fuzzyTerms = new ArrayList<>(); public Variable() { } @@ -19,9 +36,9 @@ public class Variable extends BaseEntity { this.name = name; } - public Variable(String name, List terms) { + public Variable(String name, List fuzzyTerms) { this.name = name; - this.terms = terms; + this.fuzzyTerms = fuzzyTerms; } public String getName() { @@ -32,11 +49,27 @@ public class Variable extends BaseEntity { this.name = name; } - public List getTerms() { - return terms; + public List getFuzzyTerms() { + return fuzzyTerms; } - public void setTerms(List terms) { - this.terms = terms; + public void setFuzzyTerms(List fuzzyTerms) { + this.fuzzyTerms = fuzzyTerms; + } + + public Project getProject() { + return project; + } + + public void setProject(Project project) { + this.project = project; + } + + public boolean isInput() { + return input; + } + + public void setInput(boolean input) { + this.input = input; } } \ No newline at end of file diff --git a/src/main/java/ru/ulstu/fc/rule/model/VariableForm.java b/src/main/java/ru/ulstu/fc/rule/model/VariableForm.java new file mode 100644 index 0000000..752c75c --- /dev/null +++ b/src/main/java/ru/ulstu/fc/rule/model/VariableForm.java @@ -0,0 +1,61 @@ +package ru.ulstu.fc.rule.model; + +import jakarta.validation.constraints.NotNull; +import jakarta.validation.constraints.Size; + +public class VariableForm { + private Integer id; + @NotNull + private Integer projectId; + @Size(min = 3, max = 250, message = "Длина должна быть от 3 до 250") + private String name; + + private boolean input = true; + + public VariableForm() { + } + + public VariableForm(Integer id, Integer projectId) { + this.id = id; + this.projectId = projectId; + } + + public VariableForm(Integer id, Variable variable) { + this.id = id; + this.projectId = variable.getProject().getId(); + this.name = variable.getName(); + this.input = variable.isInput(); + } + + public Integer getProjectId() { + return projectId; + } + + public Integer getId() { + return id; + } + + public void setId(Integer id) { + this.id = id; + } + + public void setProjectId(Integer projectId) { + this.projectId = projectId; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public boolean isInput() { + return input; + } + + public void setInput(boolean input) { + this.input = input; + } +} diff --git a/src/main/java/ru/ulstu/fc/rule/model/VariableValue.java b/src/main/java/ru/ulstu/fc/rule/model/VariableValue.java deleted file mode 100644 index 9bc9550..0000000 --- a/src/main/java/ru/ulstu/fc/rule/model/VariableValue.java +++ /dev/null @@ -1,30 +0,0 @@ -package ru.ulstu.fc.rule.model; - -public class VariableValue { - private String fuzzyTerm; - private Double value; - - public VariableValue() { - } - - public VariableValue(String fuzzyTerm, Double value) { - this.fuzzyTerm = fuzzyTerm; - this.value = value; - } - - public String getFuzzyTerm() { - return fuzzyTerm; - } - - public void setFuzzyTerm(String fuzzyTerm) { - this.fuzzyTerm = fuzzyTerm; - } - - public Double getValue() { - return value; - } - - public void setValue(Double value) { - this.value = value; - } -} diff --git a/src/main/java/ru/ulstu/fc/rule/repository/FuzzyRuleRepository.java b/src/main/java/ru/ulstu/fc/rule/repository/FuzzyRuleRepository.java new file mode 100644 index 0000000..deb0a71 --- /dev/null +++ b/src/main/java/ru/ulstu/fc/rule/repository/FuzzyRuleRepository.java @@ -0,0 +1,12 @@ +package ru.ulstu.fc.rule.repository; + +import org.springframework.data.jpa.repository.JpaRepository; +import ru.ulstu.fc.project.model.Project; +import ru.ulstu.fc.rule.model.FuzzyRule; + +import java.util.List; + +public interface FuzzyRuleRepository extends JpaRepository { + + List findByProject(Project project); +} diff --git a/src/main/java/ru/ulstu/fc/rule/repository/FuzzyTermRepository.java b/src/main/java/ru/ulstu/fc/rule/repository/FuzzyTermRepository.java new file mode 100644 index 0000000..54ca372 --- /dev/null +++ b/src/main/java/ru/ulstu/fc/rule/repository/FuzzyTermRepository.java @@ -0,0 +1,8 @@ +package ru.ulstu.fc.rule.repository; + +import org.springframework.data.jpa.repository.JpaRepository; +import ru.ulstu.fc.rule.model.FuzzyTerm; + +public interface FuzzyTermRepository extends JpaRepository { + +} diff --git a/src/main/java/ru/ulstu/fc/rule/repository/VariableRepository.java b/src/main/java/ru/ulstu/fc/rule/repository/VariableRepository.java index 29d1231..f228db7 100644 --- a/src/main/java/ru/ulstu/fc/rule/repository/VariableRepository.java +++ b/src/main/java/ru/ulstu/fc/rule/repository/VariableRepository.java @@ -1,7 +1,12 @@ package ru.ulstu.fc.rule.repository; import org.springframework.data.jpa.repository.JpaRepository; +import ru.ulstu.fc.project.model.Project; import ru.ulstu.fc.rule.model.Variable; +import java.util.List; + public interface VariableRepository extends JpaRepository { + + List findByProject(Project project); } diff --git a/src/main/java/ru/ulstu/fc/rule/service/FuzzyInferenceService.java b/src/main/java/ru/ulstu/fc/rule/service/FuzzyInferenceService.java index 17257e4..3812d83 100644 --- a/src/main/java/ru/ulstu/fc/rule/service/FuzzyInferenceService.java +++ b/src/main/java/ru/ulstu/fc/rule/service/FuzzyInferenceService.java @@ -14,8 +14,8 @@ import com.fuzzylite.variable.OutputVariable; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.stereotype.Service; +import ru.ulstu.fc.rule.model.FuzzyTerm; import ru.ulstu.fc.rule.model.OutputValue; -import ru.ulstu.fc.rule.model.Term; import ru.ulstu.fc.rule.model.Variable; import java.util.List; @@ -48,13 +48,16 @@ public class FuzzyInferenceService { final InputVariable input = new InputVariable(); input.setName(variable.getName()); input.setDescription(""); - input.setRange(0, variable.getTerms().get(variable.getTerms().size() - 1).getMax()); + input.setRange(0, variable.getFuzzyTerms().get(variable.getFuzzyTerms().size() - 1).getCrispValue()); input.setEnabled(true); input.setLockValueInRange(false); - for (int i = 0; i < variable.getTerms().size(); i++) { - Triangle term = new Triangle(variable.getTerms().get(i).getName(), - variable.getTerms().get(i).getMin(), - variable.getTerms().get(i).getMax()); + double prev = 0; + for (int i = 0; i < variable.getFuzzyTerms().size(); i++) { + Triangle term = new Triangle(variable.getFuzzyTerms().get(i).getDescription(), + prev, + variable.getFuzzyTerms().get(i).getCrispValue(), + variable.getFuzzyTerms().get(i).getCrispValue() + variable.getFuzzyTerms().get(i).getCrispValue() - prev); + prev = term.getVertexB(); input.addTerm(term); } return input; @@ -64,16 +67,20 @@ public class FuzzyInferenceService { final OutputVariable output = new OutputVariable(); output.setName(variable.getName()); output.setDescription(""); - output.setRange(0, variable.getTerms().get(variable.getTerms().size() - 1).getMax()); + output.setRange(0, variable.getFuzzyTerms().get(variable.getFuzzyTerms().size() - 1).getCrispValue()); output.setEnabled(true); output.setAggregation(new Maximum()); output.setDefuzzifier(new WeightedAverage()); output.setDefaultValue(Double.NaN); output.setLockValueInRange(false); - for (int i = 0; i < variable.getTerms().size(); i++) { - Triangle term = new Triangle(variable.getTerms().get(i).getName(), - variable.getTerms().get(i).getMin(), - variable.getTerms().get(i).getMax()); + double prev = 0; + for (int i = 0; i < variable.getFuzzyTerms().size(); i++) { + Triangle term = new Triangle( + variable.getFuzzyTerms().get(i).getDescription(), + prev, + variable.getFuzzyTerms().get(i).getCrispValue(), + variable.getFuzzyTerms().get(i).getCrispValue() + variable.getFuzzyTerms().get(i).getCrispValue() - prev); + prev = term.getVertexB(); output.addTerm(term); } return output; @@ -94,6 +101,7 @@ public class FuzzyInferenceService { mamdani.setImplication(new AlgebraicProduct()); mamdani.setActivation(new General()); rules.forEach(r -> mamdani.addRule(Rule.parse(r, engine))); + mamdani.addRule(new Rule()); return mamdani; } @@ -119,21 +127,20 @@ public class FuzzyInferenceService { public List getFuzzyInference(Map vals) { return getFuzzyInference(getDemoRules(), vals, List.of(new Variable("возраст", List.of( - new Term("молодой", 0.0, 40.0), - new Term("средний", 20.0, 60.0), - new Term("старый", 50.0, 100.0)) + new FuzzyTerm("молодой", 35.0), + new FuzzyTerm("средний", 60.0), + new FuzzyTerm("старый", 100.0)) ), new Variable("доход", List.of( - new Term("небольшой", 0.0, 35000.0), - new Term("средний", 20000.0, 100000.0), - new Term("высокий", 80000.0, 500000.0)) + new FuzzyTerm("небольшой", 35000.0), + new FuzzyTerm("средний", 100000.0), + new FuzzyTerm("высокий", 500000.0)) ) ), new Variable("кредит", List.of( - new Term("не_выдавать_кредит", 0.0, 1.0), - new Term("небольшой", 1.0, 50000.0), - new Term("средний", 25000.0, 100000.0), - new Term("большой", 75000.0, 1000000.0))) + new FuzzyTerm("небольшой", 20000.0), + new FuzzyTerm("средний", 100000.0), + new FuzzyTerm("большой", 1000000.0))) ); } diff --git a/src/main/java/ru/ulstu/fc/rule/service/FuzzyRuleService.java b/src/main/java/ru/ulstu/fc/rule/service/FuzzyRuleService.java new file mode 100644 index 0000000..e4df2b7 --- /dev/null +++ b/src/main/java/ru/ulstu/fc/rule/service/FuzzyRuleService.java @@ -0,0 +1,41 @@ +package ru.ulstu.fc.rule.service; + +import org.springframework.stereotype.Service; +import ru.ulstu.fc.project.service.ProjectService; +import ru.ulstu.fc.rule.model.FuzzyRule; +import ru.ulstu.fc.rule.model.FuzzyRuleForm; +import ru.ulstu.fc.rule.repository.FuzzyRuleRepository; + +@Service +public class FuzzyRuleService { + private final FuzzyRuleRepository ruleRepository; + private final ProjectService projectService; + + public FuzzyRuleService(FuzzyRuleRepository ruleRepository, ProjectService projectService) { + this.ruleRepository = ruleRepository; + this.projectService = projectService; + } + + public FuzzyRule getById(Integer id) { + return ruleRepository + .findById(id) + .orElseThrow(() -> new RuntimeException("Rule not found by id")); + } + + public Object save(FuzzyRuleForm ruleForm) { + FuzzyRule rule; + if (ruleForm.getId() == null || ruleForm.getId() == 0) { + rule = new FuzzyRule(); + } else { + rule = getById(ruleForm.getId()); + } + rule.setProject(projectService.getById(ruleForm.getProjectId())); + rule.setContent(ruleForm.getContent()); + return ruleRepository.save(rule); + } + + public void delete(FuzzyRuleForm ruleForm) { + getById(ruleForm.getId()); + ruleRepository.deleteById(ruleForm.getId()); + } +} diff --git a/src/main/java/ru/ulstu/fc/rule/service/FuzzyTermService.java b/src/main/java/ru/ulstu/fc/rule/service/FuzzyTermService.java new file mode 100644 index 0000000..a9f6d2c --- /dev/null +++ b/src/main/java/ru/ulstu/fc/rule/service/FuzzyTermService.java @@ -0,0 +1,56 @@ +package ru.ulstu.fc.rule.service; + +import org.springframework.stereotype.Service; +import ru.ulstu.fc.rule.model.FuzzyTerm; +import ru.ulstu.fc.rule.model.FuzzyTermForm; +import ru.ulstu.fc.rule.repository.FuzzyTermRepository; + +import java.util.Collections; +import java.util.List; + +@Service +public class FuzzyTermService { + private final FuzzyTermRepository fuzzyTermRepository; + private final VariableService variableService; + + public FuzzyTermService(FuzzyTermRepository fuzzyTermRepository, + VariableService variableService) { + this.fuzzyTermRepository = fuzzyTermRepository; + this.variableService = variableService; + } + + public FuzzyTerm getById(Integer id) { + return fuzzyTermRepository + .findById(id) + .orElseThrow(() -> new RuntimeException("Term not found by id")); + } + + public FuzzyTerm save(FuzzyTermForm fuzzyTermForm) { + FuzzyTerm term; + if (fuzzyTermForm.getId() == null || fuzzyTermForm.getId() == 0) { + term = new FuzzyTerm(); + } else { + term = getById(fuzzyTermForm.getId()); + } + term.setDescription(fuzzyTermForm.getDescription()); + term.setCrispValue(fuzzyTermForm.getCrispValue()); + FuzzyTerm ft = fuzzyTermRepository.save(term); + if (fuzzyTermForm.getId() == null || fuzzyTermForm.getId() == 0) { + variableService.addFuzzyTerm(fuzzyTermForm.getVariableId(), ft); + } + return ft; + + } + + public void delete(FuzzyTermForm fuzzyTermForm) { + getById(fuzzyTermForm.getId()); + fuzzyTermRepository.deleteById(fuzzyTermForm.getId()); + } + + public List getByVariableId(Integer variableId) { + if (variableId == null || variableId == 0) { + return Collections.emptyList(); + } + return variableService.getById(variableId).getFuzzyTerms(); + } +} diff --git a/src/main/java/ru/ulstu/fc/rule/service/VariableService.java b/src/main/java/ru/ulstu/fc/rule/service/VariableService.java index 7837533..5fca4eb 100644 --- a/src/main/java/ru/ulstu/fc/rule/service/VariableService.java +++ b/src/main/java/ru/ulstu/fc/rule/service/VariableService.java @@ -1,38 +1,49 @@ package ru.ulstu.fc.rule.service; import org.springframework.stereotype.Service; -import ru.ulstu.fc.rule.model.AddVariableForm; +import ru.ulstu.fc.project.service.ProjectService; +import ru.ulstu.fc.rule.model.FuzzyTerm; import ru.ulstu.fc.rule.model.Variable; +import ru.ulstu.fc.rule.model.VariableForm; import ru.ulstu.fc.rule.repository.VariableRepository; -import java.util.List; - @Service public class VariableService { private final VariableRepository variableRepository; + private final ProjectService projectService; - public VariableService(VariableRepository variableRepository) { + public VariableService(VariableRepository variableRepository, ProjectService projectService) { this.variableRepository = variableRepository; + this.projectService = projectService; } - - public List getVars() { - return variableRepository.findAll(); + public Variable getById(Integer id) { + return variableRepository + .findById(id) + .orElseThrow(() -> new RuntimeException("Variable not found by id")); } - public void save(AddVariableForm addVariableForm) { - if (addVariableForm.getId() == null) { - variableRepository.save(new Variable(addVariableForm.getName())); + public Variable save(VariableForm variableForm) { + Variable variable; + if (variableForm.getId() == null || variableForm.getId() == 0) { + variable = new Variable(); } else { - Variable dbVar = variableRepository.findById(addVariableForm.getId()).orElseThrow(() -> new RuntimeException("Variable not found by id")); - dbVar.setName(addVariableForm.getName()); - variableRepository.save(dbVar); + variable = getById(variableForm.getId()); } + variable.setProject(projectService.getById(variableForm.getProjectId())); + variable.setName(variableForm.getName()); + variable.setInput(variableForm.isInput()); + return variableRepository.save(variable); } - public AddVariableForm getAddVariableFormOrDefault(Integer id) { - return id == null - ? new AddVariableForm() - : new AddVariableForm(variableRepository.findById(id).orElseThrow(() -> new RuntimeException("Var not foubd by id"))); + public void delete(VariableForm ruleForm) { + getById(ruleForm.getId()); + variableRepository.deleteById(ruleForm.getId()); + } + + public void addFuzzyTerm(Integer variableId, FuzzyTerm ft) { + Variable var = getById(variableId); + var.getFuzzyTerms().add(ft); + variableRepository.save(var); } } diff --git a/src/main/java/ru/ulstu/fc/user/controller/UserController.java b/src/main/java/ru/ulstu/fc/user/controller/UserController.java new file mode 100644 index 0000000..a1089eb --- /dev/null +++ b/src/main/java/ru/ulstu/fc/user/controller/UserController.java @@ -0,0 +1,40 @@ +package ru.ulstu.fc.user.controller; + +import jakarta.servlet.http.HttpServletRequest; +import jakarta.validation.Valid; +import org.springframework.stereotype.Controller; +import org.springframework.ui.Model; +import org.springframework.validation.Errors; +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.context.request.WebRequest; +import ru.ulstu.fc.user.model.User; +import ru.ulstu.fc.user.model.UserDto; +import ru.ulstu.fc.user.service.UserService; + +@Controller +public class UserController { + private final UserService userService; + + public UserController(UserService userService) { + this.userService = userService; + } + + @GetMapping("/user/register") + public String showRegistrationForm(WebRequest request, Model model) { + UserDto userDto = new UserDto(); + model.addAttribute("user", userDto); + return "register"; + } + + @PostMapping("/user/register") + public String registerUserAccount( + @ModelAttribute("user") @Valid UserDto userDto, + HttpServletRequest request, + Errors errors) { + + userService.createUser(new User(userDto)); + return "redirect:/login"; + } +} diff --git a/src/main/java/ru/ulstu/fc/user/model/User.java b/src/main/java/ru/ulstu/fc/user/model/User.java new file mode 100644 index 0000000..9b1bc04 --- /dev/null +++ b/src/main/java/ru/ulstu/fc/user/model/User.java @@ -0,0 +1,77 @@ +package ru.ulstu.fc.user.model; + +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.JoinColumn; +import jakarta.persistence.JoinTable; +import jakarta.persistence.ManyToMany; +import jakarta.persistence.Table; +import jakarta.validation.constraints.NotNull; +import jakarta.validation.constraints.Pattern; +import jakarta.validation.constraints.Size; +import ru.ulstu.fc.config.Constants; +import ru.ulstu.fc.core.model.BaseEntity; + +import java.util.HashSet; +import java.util.Set; + +@Entity +@Table(name = "is_users") +public class User extends BaseEntity { + @NotNull + @Pattern(regexp = Constants.LOGIN_REGEX) + @Size(min = 1, max = 50) + @Column(length = 50, unique = true, nullable = false) + private String login; + + @NotNull + @Size(min = 60, max = 60) + @Column(name = "password_hash", length = 60, nullable = false) + private String password; + + @ManyToMany + @JoinTable( + name = "is_user_role", + joinColumns = {@JoinColumn(name = "user_id", referencedColumnName = "id")}, + inverseJoinColumns = {@JoinColumn(name = "user_role_name", referencedColumnName = "name")}) + private Set roles; + + public User() { + roles = new HashSet<>(); + } + + public User(String login, String password, Set roles) { + this.login = login; + this.password = password; + this.roles = roles; + } + + public User(UserDto userDto) { + this.login = userDto.getLogin(); + this.password = userDto.getPassword(); + } + + public String getLogin() { + return login; + } + + public void setLogin(String login) { + this.login = login.toLowerCase(); + } + + public String getPassword() { + return password; + } + + public void setPassword(String password) { + this.password = password; + } + + public Set getRoles() { + return roles; + } + + public void setRoles(Set roles) { + this.roles = roles; + } +} diff --git a/src/main/java/ru/ulstu/fc/user/model/UserDto.java b/src/main/java/ru/ulstu/fc/user/model/UserDto.java new file mode 100644 index 0000000..0a7e064 --- /dev/null +++ b/src/main/java/ru/ulstu/fc/user/model/UserDto.java @@ -0,0 +1,40 @@ +package ru.ulstu.fc.user.model; + +import jakarta.validation.constraints.NotEmpty; +import jakarta.validation.constraints.NotNull; + +public class UserDto { + @NotNull + @NotEmpty + private String login; + + @NotNull + @NotEmpty + private String password; + private String matchingPassword; + + public String getLogin() { + return login; + } + + public void setLogin(String login) { + this.login = login; + } + + public String getPassword() { + return password; + } + + public void setPassword(String password) { + this.password = password; + } + + public String getMatchingPassword() { + return matchingPassword; + } + + public void setMatchingPassword(String matchingPassword) { + this.matchingPassword = matchingPassword; + } + +} diff --git a/src/main/java/ru/ulstu/fc/user/model/UserNotFoundException.java b/src/main/java/ru/ulstu/fc/user/model/UserNotFoundException.java new file mode 100644 index 0000000..7840fc6 --- /dev/null +++ b/src/main/java/ru/ulstu/fc/user/model/UserNotFoundException.java @@ -0,0 +1,7 @@ +package ru.ulstu.fc.user.model; + +public class UserNotFoundException extends RuntimeException { + public UserNotFoundException(String message) { + super(message); + } +} diff --git a/src/main/java/ru/ulstu/fc/user/model/UserRole.java b/src/main/java/ru/ulstu/fc/user/model/UserRole.java new file mode 100644 index 0000000..c6375cd --- /dev/null +++ b/src/main/java/ru/ulstu/fc/user/model/UserRole.java @@ -0,0 +1,50 @@ +package ru.ulstu.fc.user.model; + +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.Id; +import jakarta.persistence.Table; +import jakarta.validation.constraints.NotNull; +import jakarta.validation.constraints.Size; + +@Entity +@Table(name = "is_user_roles") +public class UserRole { + @Id + @NotNull + @Size(max = 50) + @Column(length = 50, nullable = false) + private String name; + + public UserRole() { + } + + public UserRole(String name) { + this.name = name; + } + + public String getName() { + return name; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + UserRole role = (UserRole) o; + return !(name != null ? !name.equals(role.name) : role.name != null); + } + + public void setName(String name) { + this.name = name; + } + + @Override + public int hashCode() { + return name != null ? name.hashCode() : 0; + } +} diff --git a/src/main/java/ru/ulstu/fc/user/model/UserRoleConstants.java b/src/main/java/ru/ulstu/fc/user/model/UserRoleConstants.java new file mode 100644 index 0000000..4eae200 --- /dev/null +++ b/src/main/java/ru/ulstu/fc/user/model/UserRoleConstants.java @@ -0,0 +1,6 @@ +package ru.ulstu.fc.user.model; + +public class UserRoleConstants { + public static final String ADMIN = "ROLE_ADMIN"; + public static final String USER = "ROLE_USER"; +} diff --git a/src/main/java/ru/ulstu/fc/user/model/UserSession.java b/src/main/java/ru/ulstu/fc/user/model/UserSession.java new file mode 100644 index 0000000..354402d --- /dev/null +++ b/src/main/java/ru/ulstu/fc/user/model/UserSession.java @@ -0,0 +1,105 @@ +package ru.ulstu.fc.user.model; + +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.JoinColumn; +import jakarta.persistence.ManyToOne; +import jakarta.persistence.Table; +import jakarta.persistence.Temporal; +import jakarta.persistence.TemporalType; +import jakarta.validation.constraints.NotNull; +import ru.ulstu.fc.core.model.BaseEntity; + +import java.util.Date; + +@Entity +@Table(name = "is_user_sessions") +public class UserSession extends BaseEntity { + @NotNull + @Column(name = "session_id", nullable = false, unique = true) + private String sessionId; + + @NotNull + @Column(name = "ip_address", nullable = false) + private String ipAddress; + + @NotNull + @Column(nullable = false) + private String host; + + @NotNull + @Column(name = "login_time", nullable = false) + @Temporal(TemporalType.TIMESTAMP) + private Date loginTime; + + @Column(name = "logout_time") + @Temporal(TemporalType.TIMESTAMP) + private Date logoutTime; + + @ManyToOne(optional = false) + @JoinColumn(name = "user_id") + private User user; + + public UserSession() { + } + + public UserSession(String sessionId, String ipAddress, String host, User user) { + this.sessionId = sessionId; + this.ipAddress = ipAddress; + this.host = host; + this.loginTime = new Date(); + this.user = user; + } + + public String getSessionId() { + return sessionId; + } + + public String getIpAddress() { + return ipAddress; + } + + public String getHost() { + return host; + } + + public Date getLoginTime() { + return loginTime; + } + + public Date getLogoutTime() { + return logoutTime; + } + + public User getUser() { + return user; + } + + public void setSessionId(String sessionId) { + this.sessionId = sessionId; + } + + public void setIpAddress(String ipAddress) { + this.ipAddress = ipAddress; + } + + public void setHost(String host) { + this.host = host; + } + + public void setLoginTime(Date loginTime) { + this.loginTime = loginTime; + } + + public void setLogoutTime(Date logoutTime) { + this.logoutTime = logoutTime; + } + + public void setUser(User user) { + this.user = user; + } + + public void close() { + this.logoutTime = new Date(); + } +} diff --git a/src/main/java/ru/ulstu/fc/user/repository/UserRepository.java b/src/main/java/ru/ulstu/fc/user/repository/UserRepository.java new file mode 100644 index 0000000..808bac5 --- /dev/null +++ b/src/main/java/ru/ulstu/fc/user/repository/UserRepository.java @@ -0,0 +1,15 @@ +package ru.ulstu.fc.user.repository; + +import org.springframework.data.jpa.repository.EntityGraph; +import org.springframework.data.jpa.repository.JpaRepository; +import ru.ulstu.fc.user.model.User; + +public interface UserRepository extends JpaRepository { + User findOneByLoginIgnoreCase(String login); + + @EntityGraph(attributePaths = "roles") + User findOneWithRolesById(int id); + + @EntityGraph(attributePaths = "roles") + User findOneWithRolesByLogin(String login); +} diff --git a/src/main/java/ru/ulstu/fc/user/repository/UserRoleRepository.java b/src/main/java/ru/ulstu/fc/user/repository/UserRoleRepository.java new file mode 100644 index 0000000..3f03a8d --- /dev/null +++ b/src/main/java/ru/ulstu/fc/user/repository/UserRoleRepository.java @@ -0,0 +1,7 @@ +package ru.ulstu.fc.user.repository; + +import org.springframework.data.jpa.repository.JpaRepository; +import ru.ulstu.fc.user.model.UserRole; + +public interface UserRoleRepository extends JpaRepository { +} diff --git a/src/main/java/ru/ulstu/fc/user/repository/UserSessionRepository.java b/src/main/java/ru/ulstu/fc/user/repository/UserSessionRepository.java new file mode 100644 index 0000000..f4a6de4 --- /dev/null +++ b/src/main/java/ru/ulstu/fc/user/repository/UserSessionRepository.java @@ -0,0 +1,13 @@ +package ru.ulstu.fc.user.repository; + +import org.springframework.data.jpa.repository.JpaRepository; +import ru.ulstu.fc.user.model.UserSession; + +import java.util.Date; +import java.util.List; + +public interface UserSessionRepository extends JpaRepository { + UserSession findOneBySessionId(String sessionId); + + List findAllByLogoutTimeIsNullAndLoginTimeBefore(Date date); +} diff --git a/src/main/java/ru/ulstu/fc/user/service/IpAddressResolver.java b/src/main/java/ru/ulstu/fc/user/service/IpAddressResolver.java new file mode 100644 index 0000000..ae95a27 --- /dev/null +++ b/src/main/java/ru/ulstu/fc/user/service/IpAddressResolver.java @@ -0,0 +1,21 @@ +package ru.ulstu.fc.user.service; + +import jakarta.servlet.http.HttpServletRequest; + +public final class IpAddressResolver { + //private static final String CLIENT_IP_HEADER = "Client-IP"; + //private static final String FORWARDED_FOR_HEADER = "X-Forwarded-For"; + + public static String getRemoteAddr(HttpServletRequest request) { + String headerClientIp = request.getHeader(""); + String headerXForwardedFor = request.getHeader(HttpServletRequest.FORM_AUTH); + if (request.getRemoteAddr().isEmpty() && !headerClientIp.isEmpty()) { + return headerClientIp; + } + if (!headerXForwardedFor.isEmpty()) { + return headerXForwardedFor; + } + return request.getRemoteAddr(); + } + +} diff --git a/src/main/java/ru/ulstu/fc/user/service/UserService.java b/src/main/java/ru/ulstu/fc/user/service/UserService.java new file mode 100644 index 0000000..b94a49c --- /dev/null +++ b/src/main/java/ru/ulstu/fc/user/service/UserService.java @@ -0,0 +1,93 @@ +package ru.ulstu.fc.user.service; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.security.core.authority.SimpleGrantedAuthority; +import org.springframework.security.core.userdetails.UserDetails; +import org.springframework.security.core.userdetails.UserDetailsService; +import org.springframework.security.crypto.password.PasswordEncoder; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import ru.ulstu.fc.user.model.User; +import ru.ulstu.fc.user.model.UserNotFoundException; +import ru.ulstu.fc.user.model.UserRole; +import ru.ulstu.fc.user.model.UserRoleConstants; +import ru.ulstu.fc.user.repository.UserRepository; +import ru.ulstu.fc.user.repository.UserRoleRepository; +import ru.ulstu.fc.user.utils.UserUtils; + +import java.util.Collections; +import java.util.Optional; +import java.util.Set; +import java.util.stream.Collectors; + +@Service +@Transactional +public class UserService implements UserDetailsService { + private final Logger log = LoggerFactory.getLogger(UserService.class); + private final PasswordEncoder passwordEncoder; + private final UserRepository userRepository; + private final UserRoleRepository userRoleRepository; + @Value("${admin-password}") + private String adminPassword; + + public UserService(PasswordEncoder passwordEncoder, + UserRepository userRepository, + UserRoleRepository userRoleRepository) { + this.passwordEncoder = passwordEncoder; + this.userRepository = userRepository; + this.userRoleRepository = userRoleRepository; + } + + public User getUserByLogin(String login) { + return userRepository.findOneByLoginIgnoreCase(login); + } + + @Override + public UserDetails loadUserByUsername(String username) { + final User user = userRepository.findOneByLoginIgnoreCase(username); + if (user == null) { + throw new UserNotFoundException(username); + } + return new org.springframework.security.core.userdetails.User(user.getLogin(), + user.getPassword(), + Optional.ofNullable(user.getRoles()).orElse(Collections.emptySet()).stream() + .map(role -> new SimpleGrantedAuthority(role.getName())) + .collect(Collectors.toList())); + } + + public User createUser(User user) { + if (getUserByLogin(user.getLogin()) != null) { + throw new RuntimeException(user.getLogin()); + } + User dbUser = (user.getId() == null) + ? user + : getUserById(user.getId()); + //user.setRoles(Collections.singleton(new UserRole(UserRoleConstants.USER))); + dbUser.setPassword(passwordEncoder.encode(user.getPassword())); + dbUser.setLogin(user.getLogin()); + dbUser = userRepository.save(dbUser); + log.debug("Created Information for User: {}", dbUser.getLogin()); + return dbUser; + } + + public User getUserById(Integer id) { + return userRepository.findById(id).orElseThrow(() -> new RuntimeException("User not found by id")); + } + + private void createDefaultUser(String login, String userRole) { + if (getUserByLogin(login) == null) { + UserRole role = userRoleRepository.save(new UserRole(userRole.toString())); + createUser(new User(login, login.equals("admin") ? adminPassword : login, Set.of(role))); + } + } + + public void initDefaultAdmin() { + createDefaultUser("admin", UserRoleConstants.ADMIN); + } + + public User getCurrentUser() { + return getUserByLogin(UserUtils.getCurrentUserLogin()); + } +} diff --git a/src/main/java/ru/ulstu/fc/user/service/UserSessionLoginHandler.java b/src/main/java/ru/ulstu/fc/user/service/UserSessionLoginHandler.java new file mode 100644 index 0000000..971c37b --- /dev/null +++ b/src/main/java/ru/ulstu/fc/user/service/UserSessionLoginHandler.java @@ -0,0 +1,43 @@ +package ru.ulstu.fc.user.service; + +import jakarta.servlet.ServletException; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import jakarta.servlet.http.HttpSession; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.security.core.Authentication; +import org.springframework.security.web.authentication.SavedRequestAwareAuthenticationSuccessHandler; +import org.springframework.stereotype.Component; +import ru.ulstu.fc.config.Constants; + +import java.io.IOException; + +@Component +public class UserSessionLoginHandler extends SavedRequestAwareAuthenticationSuccessHandler { + private final Logger log = LoggerFactory.getLogger(UserSessionLoginHandler.class); + private final UserSessionService userSessionService; + + public UserSessionLoginHandler(UserSessionService userSessionService) { + super(); + this.userSessionService = userSessionService; + } + + @Override + public void onAuthenticationSuccess(HttpServletRequest request, + HttpServletResponse response, + Authentication authentication) throws IOException, ServletException { + super.onAuthenticationSuccess(request, response, authentication); + final String login = authentication.getName(); + final String ipAddress = IpAddressResolver.getRemoteAddr(request); + final String host = request.getRemoteHost(); + log.debug("Authentication Success for {}@{} ({})", login, ipAddress, host); + HttpSession session = request.getSession(false); + if (session != null) { + final String sessionId = session.getId(); + userSessionService.createUserSession(sessionId, login, ipAddress, host); + session.setAttribute(Constants.SESSION_ID_ATTR, sessionId); + session.setMaxInactiveInterval(Constants.SESSION_TIMEOUT_SECONDS); + } + } +} diff --git a/src/main/java/ru/ulstu/fc/user/service/UserSessionLogoutHandler.java b/src/main/java/ru/ulstu/fc/user/service/UserSessionLogoutHandler.java new file mode 100644 index 0000000..662c11e --- /dev/null +++ b/src/main/java/ru/ulstu/fc/user/service/UserSessionLogoutHandler.java @@ -0,0 +1,47 @@ +package ru.ulstu.fc.user.service; + +import jakarta.servlet.ServletException; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import jakarta.servlet.http.HttpSession; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.security.core.Authentication; +import org.springframework.security.web.authentication.logout.SimpleUrlLogoutSuccessHandler; +import org.springframework.stereotype.Component; +import ru.ulstu.fc.config.Constants; + +import java.io.IOException; + +@Component +public class UserSessionLogoutHandler extends SimpleUrlLogoutSuccessHandler { + private final Logger log = LoggerFactory.getLogger(UserSessionLogoutHandler.class); + private final UserSessionService userSessionService; + + public UserSessionLogoutHandler(UserSessionService userSessionService) { + this.userSessionService = userSessionService; + setDefaultTargetUrl(Constants.LOGOUT_URL); + } + + @Override + public void onLogoutSuccess(HttpServletRequest request, + HttpServletResponse response, + Authentication authentication) throws IOException, ServletException { + if (authentication == null) { + super.onLogoutSuccess(request, response, authentication); + return; + } + final String login = authentication.getName(); + final String ipAddress = IpAddressResolver.getRemoteAddr(request); + final String host = request.getRemoteHost(); + log.debug("Logout Success for {}@{} ({})", login, ipAddress, host); + HttpSession session = request.getSession(false); + if (session != null) { + final String sessionId = session.getAttribute(Constants.SESSION_ID_ATTR).toString(); + userSessionService.closeUserSession(sessionId); + session.removeAttribute(Constants.SESSION_ID_ATTR); + session.invalidate(); + } + super.onLogoutSuccess(request, response, authentication); + } +} diff --git a/src/main/java/ru/ulstu/fc/user/service/UserSessionService.java b/src/main/java/ru/ulstu/fc/user/service/UserSessionService.java new file mode 100644 index 0000000..037957c --- /dev/null +++ b/src/main/java/ru/ulstu/fc/user/service/UserSessionService.java @@ -0,0 +1,42 @@ +package ru.ulstu.fc.user.service; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import ru.ulstu.fc.user.model.User; +import ru.ulstu.fc.user.model.UserNotFoundException; +import ru.ulstu.fc.user.model.UserSession; +import ru.ulstu.fc.user.repository.UserSessionRepository; + +@Service +@Transactional +public class UserSessionService { + private final Logger log = LoggerFactory.getLogger(UserSessionService.class); + private final UserSessionRepository userSessionRepository; + private final UserService userService; + + public UserSessionService(UserSessionRepository userSessionRepository, UserService userService) { + this.userSessionRepository = userSessionRepository; + this.userService = userService; + } + + public void createUserSession(String sessionId, String login, String ipAddress, String host) { + final User user = userService.getUserByLogin(login); + if (user == null) { + throw new UserNotFoundException(login); + } + userSessionRepository.save(new UserSession(sessionId, ipAddress, host, user)); + log.debug("User session {} created for user {}@{} ({})", sessionId, login, ipAddress, host); + } + + public void closeUserSession(String sessionId) { + final UserSession userSession = userSessionRepository.findOneBySessionId(sessionId); + if (userSession == null) { + throw new IllegalArgumentException(String.format("User session %s not found", sessionId)); + } + userSession.close(); + userSessionRepository.save(userSession); + log.debug("User session {} closed", sessionId); + } +} diff --git a/src/main/java/ru/ulstu/fc/user/utils/UserUtils.java b/src/main/java/ru/ulstu/fc/user/utils/UserUtils.java new file mode 100644 index 0000000..fc610b7 --- /dev/null +++ b/src/main/java/ru/ulstu/fc/user/utils/UserUtils.java @@ -0,0 +1,24 @@ +package ru.ulstu.fc.user.utils; + +import org.springframework.security.core.Authentication; +import org.springframework.security.core.context.SecurityContext; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.security.core.userdetails.UserDetails; + +public class UserUtils { + public static String getCurrentUserLogin() { + final SecurityContext securityContext = SecurityContextHolder.getContext(); + if (securityContext == null) { + return null; + } + final Authentication authentication = securityContext.getAuthentication(); + if (authentication.getPrincipal() instanceof UserDetails) { + final UserDetails springSecurityUser = (UserDetails) authentication.getPrincipal(); + return springSecurityUser.getUsername(); + } + if (authentication.getPrincipal() instanceof String) { + return (String) authentication.getPrincipal(); + } + return null; + } +} diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties index 7c1eddc..05d3866 100644 --- a/src/main/resources/application.properties +++ b/src/main/resources/application.properties @@ -1,11 +1,14 @@ spring.main.banner-mode=off server.port=8080 server.jetty.connection-idle-timeout=1000s +admin-password=admin # Available levels are: TRACE, DEBUG, INFO, WARN, ERROR, FATAL, OFF logging.level.ru.ulstu=DEBUG logging.level.sun.rmi.transport=off logging.level.javax.management.remote.rmi=off logging.level.java.rmi.server=off +logging.level.org.apache.tomcat=INFO +logging.level.org.apache.tomcat.util.net=WARN extractor.custom-projects-dir= server.error.include-stacktrace=always server.error.include-exception=true @@ -18,6 +21,4 @@ spring.datasource.password=password spring.jpa.database-platform=org.hibernate.dialect.H2Dialect spring.h2.console.enabled=true spring.jpa.hibernate.ddl-auto=update -# swagger-ui custom path -springdoc.swagger-ui.path=/swagger-ui.html diff --git a/src/main/resources/templates/default.html b/src/main/resources/templates/default.html index eea7eff..d4459fb 100644 --- a/src/main/resources/templates/default.html +++ b/src/main/resources/templates/default.html @@ -1,6 +1,7 @@ - + + Нечеткий контроллер @@ -12,6 +13,7 @@ +

@@ -56,4 +67,5 @@ + \ No newline at end of file diff --git a/src/main/resources/templates/error/404.html b/src/main/resources/templates/error/404.html new file mode 100644 index 0000000..d599650 --- /dev/null +++ b/src/main/resources/templates/error/404.html @@ -0,0 +1,13 @@ + + + + + +
+
Страница не найдена
+
Вернуться на главную
+
+ + \ No newline at end of file diff --git a/src/main/resources/templates/error/500.html b/src/main/resources/templates/error/500.html new file mode 100644 index 0000000..b77a6cf --- /dev/null +++ b/src/main/resources/templates/error/500.html @@ -0,0 +1,13 @@ + + + + + +
+
Ошибка сервера
+
Вернуться на главную
+
+ + \ No newline at end of file diff --git a/src/main/resources/templates/fuzzyTerm/edit.html b/src/main/resources/templates/fuzzyTerm/edit.html new file mode 100644 index 0000000..1663feb --- /dev/null +++ b/src/main/resources/templates/fuzzyTerm/edit.html @@ -0,0 +1,54 @@ + + + + Редактирование терма + + +
+

Редактирование терма:

+
+ + + +
+ + +

+ Не может быть пустым +

+
+ +
+ + +

+ Не может быть пустым +

+
+ + + + Отмена +
+
+ diff --git a/src/main/resources/templates/index.html b/src/main/resources/templates/index.html index fbeb483..b1a1070 100644 --- a/src/main/resources/templates/index.html +++ b/src/main/resources/templates/index.html @@ -25,11 +25,11 @@
@@ -40,11 +40,11 @@
diff --git a/src/main/resources/templates/listRules.html b/src/main/resources/templates/listRules.html index 2b93617..e69de29 100644 --- a/src/main/resources/templates/listRules.html +++ b/src/main/resources/templates/listRules.html @@ -1,42 +0,0 @@ - - - - Список правил - - -
- - - - - - - - - - - - - - - - - - - - -
Правила
Еслиито - - - - - - - -
- Добавить правило -
- diff --git a/src/main/resources/templates/login.html b/src/main/resources/templates/login.html new file mode 100644 index 0000000..a903e3b --- /dev/null +++ b/src/main/resources/templates/login.html @@ -0,0 +1,35 @@ + + + + + + +
+
+
+
+
+
+ +
+
+ +
+ + Регистрация +
+
+
+
+
+ + \ No newline at end of file diff --git a/src/main/resources/templates/loginError.html b/src/main/resources/templates/loginError.html new file mode 100644 index 0000000..fe97e58 --- /dev/null +++ b/src/main/resources/templates/loginError.html @@ -0,0 +1,39 @@ + + + + + + +
+
+ +
+
+
+
+
+
+ +
+
+ +
+ +
+
+
+
+
+ + \ No newline at end of file diff --git a/src/main/resources/templates/project/edit.html b/src/main/resources/templates/project/edit.html new file mode 100644 index 0000000..3a138d2 --- /dev/null +++ b/src/main/resources/templates/project/edit.html @@ -0,0 +1,86 @@ + + + + + Редактирование проекта + + +
+

Редактирование проекта:

+
+ +
+ + +

+ Не может быть пустым +

+
+
+ +
+ + + Отмена +
+ +
+
+
+

Список переменных

+
+
+
+ + + +
+
+
+ Добавить преременную +
+
+

Список правил

+
+
+
+ + + +
+
+ +
+ Добавить правило +
+
+ + \ No newline at end of file diff --git a/src/main/resources/templates/project/list.html b/src/main/resources/templates/project/list.html new file mode 100644 index 0000000..e5f6165 --- /dev/null +++ b/src/main/resources/templates/project/list.html @@ -0,0 +1,22 @@ + + + + Список проектов + + + + diff --git a/src/main/resources/templates/project/listRules.html b/src/main/resources/templates/project/listRules.html new file mode 100644 index 0000000..04a8a7f --- /dev/null +++ b/src/main/resources/templates/project/listRules.html @@ -0,0 +1,30 @@ + + + + + Список правил + + +
+

Список правил

+
+
+ 1. Если +
+
+ Переменная +
+
+ есть +
+
+ значение +
+
+ И / ИЛИ +
+
+
+ + \ No newline at end of file diff --git a/src/main/resources/templates/register.html b/src/main/resources/templates/register.html new file mode 100644 index 0000000..a622908 --- /dev/null +++ b/src/main/resources/templates/register.html @@ -0,0 +1,29 @@ + + +
+

Регистрация

+
+
+ + +

Validation error

+
+
+ + +

Validation error

+
+
+ + +
+ + Вернуться на страницу входа +
+
+ + + \ No newline at end of file diff --git a/src/main/resources/templates/rule/edit.html b/src/main/resources/templates/rule/edit.html new file mode 100644 index 0000000..8c8b865 --- /dev/null +++ b/src/main/resources/templates/rule/edit.html @@ -0,0 +1,38 @@ + + + + Редактирование правила + + +
+

Редактирование правила:

+
+ + +
+ + +

+ Не может быть пустым +

+
+ + + + Отмена +
+
+ diff --git a/src/main/resources/templates/variable/edit.html b/src/main/resources/templates/variable/edit.html new file mode 100644 index 0000000..2f9979d --- /dev/null +++ b/src/main/resources/templates/variable/edit.html @@ -0,0 +1,55 @@ + + + + + Редактирование переменной + + +
+

Редактирование переменной:

+
+ + +
+ + +

+ Не может быть пустым +

+
+ +
+ + +
+ + + + Отмена + +
+
+
+

Список термов

+
+
+
+ + + +
+
+
+ Добавить терм +
+
+
+
+ + \ No newline at end of file