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 29953ea..a4b76b9 100644 Binary files a/gradle/wrapper/gradle-wrapper.jar and b/gradle/wrapper/gradle-wrapper.jar differ diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index e750102..e2847c8 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,7 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-7.3-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.11.1-bin.zip +networkTimeout=10000 +validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/open.png b/open.png new file mode 100644 index 0000000..b6d42cc Binary files /dev/null and b/open.png differ diff --git a/run.png b/run.png new file mode 100644 index 0000000..33e732a Binary files /dev/null and b/run.png differ 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