diff --git a/docs/states.puml b/docs/states.puml index 15d3ec5..e7a1933 100644 --- a/docs/states.puml +++ b/docs/states.puml @@ -5,31 +5,33 @@ left to right direction [*] --> mainframe : exam-main (localhost:100) ' mainframe -state "mainframe" as mainframe { - state "fragment: authorization" as authorization{ - authorization: - поле для внесения кода попытки - authorization: - кнопка перехода к продолжению существующей попытки (курсу) - authorization: фрагмент может быть скрыт от пользователя, если хотим исключить \nвозможность входа по коду попытки - +state "Страница входа (mainframe)" as mainframe { + state "fragment: login" as login{ + login: - поле для внесения кода попытки + login: - кнопка перехода к продолжению существующей попытки (курсу) + login: фрагмент может быть скрыт от пользователя, если хотим исключить \nвозможность входа по коду попытки + state "hidden: Аутентификация" as authentification + state "hidden: Авторизация" as authorization + state "hidden: session active" as active } state "fragment: registration" as registration { registration: - селект наименования организации registration: - селект наименования должности registration: - кнопка перехода к новой попытке (курсу) } - state "fragment: hello" as hello { + state "static: hello" as hello { hello : - Приветствие (*.md статический контент) hello : - Коментарии для новых пользователей (*.md статический контент) hello : - Пояснения по обработке персональных данных (*.md статический контент) } - state "hidden: session active" as active } +authorization --> access : id аккаунта registration --> access : наименование организации и должность -authorization --> access : код попытки + active --> access : id текущей сессии -state "Проверка доступа" as access { +state "Проверка доступа (access)" as access { access: проверка наличия кода попытки в базе access: проверка наличия текущей сессии в базе access: создание нового аккаунта @@ -39,7 +41,7 @@ state "Проверка доступа" as access { access --> mainframe : запрошенный аккаунт отсутствует access --> account : направление пользователя к запрошенному аккаунту -state "Назначенные курсы" as account { +state "Аккаунт (account)" as account { account: - ID попытки account: - перечень доступных курсов account: - выход из id (сброс сессии) @@ -60,9 +62,9 @@ state "Назначенные курсы" as account { exit --> mainframe : сброс сессии и возврат на главную страницу -state "Курс" as course { +state "Курс (course)" as course { - state "fragment: content" as courseContent{ + state "static: content" as courseContent { courseContent: статический контент *.md courseContent: - img courseContent: - video @@ -89,7 +91,7 @@ courseControls --> account : переход к списку доступных courseControls --> question : запрос первого вопроса quiz --> question : следющий вопрос -state "Вопос" as question { +state "Вопос (question)" as question { question: тело вопроса question: варианты ответа question: переход к следующему вопросу diff --git a/main/src/main/java/gsp/technologies/main/access/login/AuthController.java b/main/src/main/java/gsp/technologies/main/access/login/AuthController.java new file mode 100644 index 0000000..e06819c --- /dev/null +++ b/main/src/main/java/gsp/technologies/main/access/login/AuthController.java @@ -0,0 +1,58 @@ +package gsp.technologies.main.access.login; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.stereotype.Controller; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.context.request.RequestContextHolder; + +import gsp.technologies.main.mainframe.MainframeController; + +/** + * Контроллер авторизации пользователя + * - обработка запросов с учетными данными пользователя (id аккаунта) + * - обработка запросов с данным сессии (id сессии) + * + * сюда обращаются сервисы, которые хотят получить доступ к аккаунту + * - mainframe (содержит поля для перехода к аккаунту) + * - login (разработка формы пока не предусмотрена) + */ +@Controller +@RequestMapping(path = "/auth") +public class AuthController { + private static final Logger log = LoggerFactory.getLogger(MainframeController.class); + + /** + * Проверка наличия аккаунта в базе. + * В метод передается номер аккаунта в 35-ричной системе счисления + * номер аккаунта декодируется и направляется запрос на проверку в базу + * при отсутствии аккаунта в базе возвращается соответствующее сообщение + * при наличии аккаунта в базе возвращаются данные для перехода к запрошенному аккаунту + * + * @return + * + */ + @GetMapping("/check") + public AuthDTO checkAccount(@RequestParam("account") String account) { + log.info("GET /auth/check"); + log.info("Запрошен номер аккаунта: {}", account); + //сравнить номер сессии с имеющимися в базе + //при наличии вернуть порядковый номер существующей сессии (в 35-ричной системе счисления) + //при отсутствии внести новую запись в базу и вернуть ее порядковый номер + return new AuthDTO(); + } + + @GetMapping("/current") + public AuthDTO checkSession(@RequestParam("session") String session) { + log.info("GET /auth/current"); + log.info("Запрошен номер сессии: {}", session); + + + return new AuthDTO(); + } + +} diff --git a/main/src/main/java/gsp/technologies/main/access/login/AuthDTO.java b/main/src/main/java/gsp/technologies/main/access/login/AuthDTO.java new file mode 100644 index 0000000..cc772b7 --- /dev/null +++ b/main/src/main/java/gsp/technologies/main/access/login/AuthDTO.java @@ -0,0 +1,25 @@ +package gsp.technologies.main.access.login; + +import java.io.Serializable; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +/** + * DTO для обмена данными с сервисом авторизации + * хранит: + * - номер сессии пользователя + * - номер запрошенного аккаунта + */ + +@AllArgsConstructor +@NoArgsConstructor +@Data +public class AuthDTO implements Serializable { + + private String sessionId; + private String accountId; + private String code35; + +} diff --git a/main/src/main/java/gsp/technologies/main/access/logout/LogoutController.java b/main/src/main/java/gsp/technologies/main/access/logout/LogoutController.java new file mode 100644 index 0000000..d89a3c0 --- /dev/null +++ b/main/src/main/java/gsp/technologies/main/access/logout/LogoutController.java @@ -0,0 +1,33 @@ +package gsp.technologies.main.access.logout; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.stereotype.Controller; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.context.request.RequestContextHolder; +import org.springframework.web.context.request.ServletRequestAttributes; + +/** + * Контроллер для выхода из системы + */ +@Controller +@RequestMapping(path = "/logout") +public class LogoutController { + private static final Logger log = LoggerFactory.getLogger(LogoutController.class); + + @GetMapping("") + public String mainframe() { + log.info("GET /logout"); + log.info("текущая сессия: {}", RequestContextHolder.currentRequestAttributes().getSessionId()); + + //logout + ServletRequestAttributes attr = (ServletRequestAttributes) RequestContextHolder.currentRequestAttributes(); + attr.getRequest().getSession().invalidate(); + log.info("новая сессия: {}", RequestContextHolder.currentRequestAttributes().getSessionId()); + + //возвращаем пользователя на исходную страницу, с новой сссией + String referer = attr.getRequest().getHeader("Referer"); + return "redirect:"+ referer; + } +} diff --git a/main/src/main/java/gsp/technologies/main/account/AccountController.java b/main/src/main/java/gsp/technologies/main/account/AccountController.java new file mode 100644 index 0000000..1d2aaa9 --- /dev/null +++ b/main/src/main/java/gsp/technologies/main/account/AccountController.java @@ -0,0 +1,73 @@ +package gsp.technologies.main.account; + +import java.util.List; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.stereotype.Controller; +import org.springframework.ui.Model; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.context.request.RequestContextHolder; + +import gsp.technologies.main.code.Code; + +/** + * Контроллер формы аккаунта + * возвращает thymeleaf шаблон + */ +@Controller +@RequestMapping(path = "/account") +public class AccountController { + private static final Logger log = LoggerFactory.getLogger(AccountController.class); + + @GetMapping("") + public String account(Model model) { + log.info("GET /account"); + //данные аккаунта (id, organization, position) + Long id = 1L; + String code = Code.encode(id); + OrganizationDTO organization = OrganizationDTO.builder() + .id(1L) + .name("ГСП-Т") + .build(); + PositionDTO position = PositionDTO.builder() + .id(1L) + .name("Сварщик") + .organization(organization) + .build(); + String sessionid = RequestContextHolder.currentRequestAttributes().getSessionId(); + List courses = List.of( + CourseDTO.builder() + .id(1L) + .name("Охрана труда") + .passed(true) + .build(), + CourseDTO.builder() + .id(2L) + .name("Работы на высоте") + .passed(false) + .build(), + CourseDTO.builder() + .id(3L) + .name("Первая помощь") + .passed(true) + .build() + ); + AccountDTO account = AccountDTO.builder() + .id(id) + .code(code) + .position(position) + .sessionid(sessionid) + .courses(courses) + .build(); + + model.addAttribute("account", account); + log.info("account: " + account); + + //перечень назначенных курсов + // ... + + return "account"; + } +} diff --git a/main/src/main/java/gsp/technologies/main/account/AccountDTO.java b/main/src/main/java/gsp/technologies/main/account/AccountDTO.java new file mode 100644 index 0000000..6121dc2 --- /dev/null +++ b/main/src/main/java/gsp/technologies/main/account/AccountDTO.java @@ -0,0 +1,21 @@ +package gsp.technologies.main.account; + +import java.io.Serializable; +import java.util.List; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Builder +@NoArgsConstructor +@AllArgsConstructor +@Data +public class AccountDTO implements Serializable { + private Long id; + private String code; + private PositionDTO position; + private String sessionid; + private List courses; +} diff --git a/main/src/main/java/gsp/technologies/main/account/CourseDTO.java b/main/src/main/java/gsp/technologies/main/account/CourseDTO.java new file mode 100644 index 0000000..9ea89d6 --- /dev/null +++ b/main/src/main/java/gsp/technologies/main/account/CourseDTO.java @@ -0,0 +1,19 @@ +package gsp.technologies.main.account; + +import java.io.Serializable; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +@NoArgsConstructor +@AllArgsConstructor +@Builder +@Data +public class CourseDTO implements Serializable { + + private Long id; + private String name; + private Boolean passed; +} diff --git a/main/src/main/java/gsp/technologies/main/account/OrganizationDTO.java b/main/src/main/java/gsp/technologies/main/account/OrganizationDTO.java new file mode 100644 index 0000000..5193082 --- /dev/null +++ b/main/src/main/java/gsp/technologies/main/account/OrganizationDTO.java @@ -0,0 +1,15 @@ +package gsp.technologies.main.account; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +@NoArgsConstructor +@AllArgsConstructor +@Builder +@Data +public class OrganizationDTO { + private Long id; + private String name; //наименование организации +} diff --git a/main/src/main/java/gsp/technologies/main/account/PositionDTO.java b/main/src/main/java/gsp/technologies/main/account/PositionDTO.java new file mode 100644 index 0000000..c1bb19d --- /dev/null +++ b/main/src/main/java/gsp/technologies/main/account/PositionDTO.java @@ -0,0 +1,18 @@ +package gsp.technologies.main.account; + +import java.io.Serializable; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +@NoArgsConstructor +@AllArgsConstructor +@Builder +@Data +public class PositionDTO implements Serializable { + private Long id; + private String name; + private OrganizationDTO organization; +} diff --git a/main/src/main/java/gsp/technologies/main/code/Code.java b/main/src/main/java/gsp/technologies/main/code/Code.java new file mode 100644 index 0000000..bb4a3dd --- /dev/null +++ b/main/src/main/java/gsp/technologies/main/code/Code.java @@ -0,0 +1,17 @@ +package gsp.technologies.main.code; + +/** + * Класс, содержащий методы преобразования id в 35-ричное представление + */ +public abstract class Code { + public static String encode(Long value) { + String code = value.toString(); + return code; + } + + public static Long decode(String code) { + Long value = Long.valueOf(code); + return value; + } + +} diff --git a/main/src/main/java/gsp/technologies/main/controllers/MainframeController.java b/main/src/main/java/gsp/technologies/main/controllers/MainframeController.java deleted file mode 100644 index 9b4ef5c..0000000 --- a/main/src/main/java/gsp/technologies/main/controllers/MainframeController.java +++ /dev/null @@ -1,53 +0,0 @@ -package gsp.technologies.main.controllers; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.stereotype.Controller; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.context.request.RequestContextHolder; -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.PostMapping; -import org.springframework.web.bind.annotation.RequestBody; - - -/** - * Контроллер для главной страницы: - * - приветствие - * - пояснения - * - переход к учебному курсу moodle - * - переход к учебному курсу custom - */ -@Controller -@RequestMapping(path = "/mainframe") -public class MainframeController { - private static final Logger log = LoggerFactory.getLogger(MainframeController.class); - - @GetMapping("") - public String mainframe() { - log.info("GET /mainframe"); - log.info("текущая сессия: {}", RequestContextHolder.currentRequestAttributes().getSessionId()); - //сравнить номер сессии с имеющимися в базе - //при наличии вернуть порядковый номер существующей сессии (в 35-ричной системе счисления) - //при отсутствии внести новую запись в базу и вернуть ее порядковый номер - - return "mainframe"; - } - - @PostMapping("/moodle") - public String moodle(@RequestBody String entity) { - //TODO: process POST request - - return entity; - } - - @PostMapping("/exam") - public String exam(@RequestBody String entity) { - //TODO: process POST request - - return entity; - } - - - - -} diff --git a/main/src/main/java/gsp/technologies/main/course/CourseController.java b/main/src/main/java/gsp/technologies/main/course/CourseController.java new file mode 100644 index 0000000..b8d92c8 --- /dev/null +++ b/main/src/main/java/gsp/technologies/main/course/CourseController.java @@ -0,0 +1,22 @@ +package gsp.technologies.main.course; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.stereotype.Controller; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RequestMapping; + +@Controller +@RequestMapping(path = "/courses") +public class CourseController { + private static final Logger log = LoggerFactory.getLogger(CourseController.class); + + @GetMapping("/view/{id}") + public String course(@PathVariable Long id) { + log.info("GET /courses"); + + return "course"; + } + +} diff --git a/main/src/main/java/gsp/technologies/main/controllers/AuthController.java b/main/src/main/java/gsp/technologies/main/mainframe/MainframeController.java similarity index 65% rename from main/src/main/java/gsp/technologies/main/controllers/AuthController.java rename to main/src/main/java/gsp/technologies/main/mainframe/MainframeController.java index 660aa08..f3c97fb 100644 --- a/main/src/main/java/gsp/technologies/main/controllers/AuthController.java +++ b/main/src/main/java/gsp/technologies/main/mainframe/MainframeController.java @@ -1,29 +1,37 @@ -package gsp.technologies.main.controllers; +package gsp.technologies.main.mainframe; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.stereotype.Controller; -import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.context.request.RequestContextHolder; +import org.springframework.web.context.request.ServletRequestAttributes; + +import jakarta.servlet.ServletException; + +import org.springframework.web.bind.annotation.GetMapping; /** - * Контроллер авторизации пользователя + * Контроллер для главной страницы: + * - приветствие / пояснения + * - форма регистрации + * - форма авторизации/login + * */ @Controller -@RequestMapping(path = "/auth") -public class AuthController { +@RequestMapping(path = "/mainframe") +public class MainframeController { private static final Logger log = LoggerFactory.getLogger(MainframeController.class); @GetMapping("") public String mainframe() { - log.info("GET /auth"); + log.info("GET /mainframe"); log.info("текущая сессия: {}", RequestContextHolder.currentRequestAttributes().getSessionId()); + //сравнить номер сессии с имеющимися в базе //при наличии вернуть порядковый номер существующей сессии (в 35-ричной системе счисления) //при отсутствии внести новую запись в базу и вернуть ее порядковый номер - return "auth"; + return "mainframe"; } - } diff --git a/main/src/main/resources/static/content/courses/1/main.md b/main/src/main/resources/static/content/courses/1/main.md new file mode 100644 index 0000000..73dc7aa --- /dev/null +++ b/main/src/main/resources/static/content/courses/1/main.md @@ -0,0 +1,2 @@ +# Первая помощь +![pic](/content/courses/1/pictures/states.png) \ No newline at end of file diff --git a/main/src/main/resources/static/content/courses/1/pictures/states.png b/main/src/main/resources/static/content/courses/1/pictures/states.png new file mode 100644 index 0000000..8ee1be6 Binary files /dev/null and b/main/src/main/resources/static/content/courses/1/pictures/states.png differ diff --git a/main/src/main/resources/templates/account.html b/main/src/main/resources/templates/account.html new file mode 100644 index 0000000..460e7b5 --- /dev/null +++ b/main/src/main/resources/templates/account.html @@ -0,0 +1,31 @@ + + + + + + exam-account + + + + +
+ +
+
+ + +
+ +
+ +
+
+
+ \ No newline at end of file diff --git a/main/src/main/resources/templates/course.html b/main/src/main/resources/templates/course.html new file mode 100644 index 0000000..0ff020d --- /dev/null +++ b/main/src/main/resources/templates/course.html @@ -0,0 +1,31 @@ + + + + + + exam-account + + + + +
+ + +
+ + +
+ +
+ + +
+ \ No newline at end of file diff --git a/main/src/main/resources/templates/fragments/account.html b/main/src/main/resources/templates/fragments/account.html new file mode 100644 index 0000000..ede21cb --- /dev/null +++ b/main/src/main/resources/templates/fragments/account.html @@ -0,0 +1,46 @@ + + + + + +
+
+ + ID: + + +
+ + +
+
+ + + + + + + + + + + + + +
номернаименованиестатус
+ + +
+ пройден + не пройден +
+
+
+ + \ No newline at end of file diff --git a/main/src/main/resources/templates/fragments/common/footer.html b/main/src/main/resources/templates/fragments/common/footer.html index 2aef342..5660cba 100644 --- a/main/src/main/resources/templates/fragments/common/footer.html +++ b/main/src/main/resources/templates/fragments/common/footer.html @@ -3,7 +3,7 @@

- © 2024 ООО "ГСП-Технологии" : gsp.technologies.exam.main + © 2024 ООО "ГСП-Технологии" : gsp.technologies.exam

diff --git a/main/src/main/resources/templates/fragments/controls.html b/main/src/main/resources/templates/fragments/controls.html index 8ac0720..ace6c56 100644 --- a/main/src/main/resources/templates/fragments/controls.html +++ b/main/src/main/resources/templates/fragments/controls.html @@ -34,7 +34,7 @@

Укажите данные для создания нового задания

-
+
- +
@@ -61,10 +61,17 @@
- +
+ +
+
+
+ +
+
\ No newline at end of file diff --git a/main/src/main/resources/templates/fragments/course.html b/main/src/main/resources/templates/fragments/course.html new file mode 100644 index 0000000..350d5d3 --- /dev/null +++ b/main/src/main/resources/templates/fragments/course.html @@ -0,0 +1,10 @@ + + + + +
+
+ +
+ + \ No newline at end of file diff --git a/main/src/main/resources/templates/mainframe.html b/main/src/main/resources/templates/mainframe.html index b059c3e..769f678 100644 --- a/main/src/main/resources/templates/mainframe.html +++ b/main/src/main/resources/templates/mainframe.html @@ -19,7 +19,7 @@ xmlns:th="http://www.thymeleaf.org">
\ No newline at end of file diff --git a/out/docs/states/states.png b/out/docs/states/states.png index 2eb0f91..8ee1be6 100644 Binary files a/out/docs/states/states.png and b/out/docs/states/states.png differ