From 653d4e89ca929dc3381a1529cc98ff28dba69965 Mon Sep 17 00:00:00 2001 From: akhmelev Date: Mon, 12 Jan 2026 18:51:28 +0300 Subject: [PATCH 01/50] 3.0 ADD Bootstrap template Superhero --- pom.xml | 2 +- .../javarush/khmelov/service/UserService.java | 2 +- src/main/webapp/WEB-INF/edit-user.jsp | 5 +-- src/main/webapp/WEB-INF/list-user.jsp | 4 +-- src/main/webapp/WEB-INF/parts/footer.jsp | 25 +++++++++++++++ src/main/webapp/WEB-INF/parts/header.jsp | 32 +++++++++++++++++++ src/main/webapp/WEB-INF/start-page.jsp | 4 +-- src/main/webapp/assets/css/styles.min.css | 1 + 8 files changed, 67 insertions(+), 8 deletions(-) create mode 100644 src/main/webapp/WEB-INF/parts/footer.jsp create mode 100644 src/main/webapp/WEB-INF/parts/header.jsp create mode 100644 src/main/webapp/assets/css/styles.min.css diff --git a/pom.xml b/pom.xml index 78ee59d..81a6d68 100644 --- a/pom.xml +++ b/pom.xml @@ -5,7 +5,7 @@ 4.0.0 com.javarush.khmelov - project-ledzeppelin + project-pantera 1.0-SNAPSHOT ProjectLedzeppelin war diff --git a/src/main/java/com/javarush/khmelov/service/UserService.java b/src/main/java/com/javarush/khmelov/service/UserService.java index b17527c..32011dc 100644 --- a/src/main/java/com/javarush/khmelov/service/UserService.java +++ b/src/main/java/com/javarush/khmelov/service/UserService.java @@ -1,7 +1,7 @@ package com.javarush.khmelov.service; -import com.javarush.khmelov.entity.User; import com.javarush.khmelov.repository.UserRepository; +import com.javarush.khmelov.entity.User; import java.util.Collection; import java.util.Optional; diff --git a/src/main/webapp/WEB-INF/edit-user.jsp b/src/main/webapp/WEB-INF/edit-user.jsp index f274104..01cca07 100644 --- a/src/main/webapp/WEB-INF/edit-user.jsp +++ b/src/main/webapp/WEB-INF/edit-user.jsp @@ -1,5 +1,5 @@ <%@ page contentType="text/html;charset=UTF-8" language="java" %> -<%@include file="head.jsp" %> +<%@include file="parts/header.jsp" %>
@@ -69,5 +69,6 @@
- +<%@include file="parts/footer.jsp" %> + diff --git a/src/main/webapp/WEB-INF/list-user.jsp b/src/main/webapp/WEB-INF/list-user.jsp index dd52c55..4bf2d26 100644 --- a/src/main/webapp/WEB-INF/list-user.jsp +++ b/src/main/webapp/WEB-INF/list-user.jsp @@ -1,9 +1,9 @@ <%@ page contentType="text/html;charset=UTF-8" language="java" %> -<%@include file="head.jsp"%> +<%@include file="parts/header.jsp" %> ${user.login} - +<%@include file="parts/footer.jsp" %> diff --git a/src/main/webapp/WEB-INF/parts/footer.jsp b/src/main/webapp/WEB-INF/parts/footer.jsp new file mode 100644 index 0000000..d7b1f31 --- /dev/null +++ b/src/main/webapp/WEB-INF/parts/footer.jsp @@ -0,0 +1,25 @@ +<%@ page contentType="text/html;charset=UTF-8" language="java" %> +<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %> + + + \ No newline at end of file diff --git a/src/main/webapp/WEB-INF/parts/header.jsp b/src/main/webapp/WEB-INF/parts/header.jsp new file mode 100644 index 0000000..2b68d57 --- /dev/null +++ b/src/main/webapp/WEB-INF/parts/header.jsp @@ -0,0 +1,32 @@ +<%@ page contentType="text/html;charset=UTF-8" language="java" %> +<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %> + + + + + + + Gorillaz + + + + + + +
+ +
\ No newline at end of file diff --git a/src/main/webapp/WEB-INF/start-page.jsp b/src/main/webapp/WEB-INF/start-page.jsp index 0531c1c..fee54be 100644 --- a/src/main/webapp/WEB-INF/start-page.jsp +++ b/src/main/webapp/WEB-INF/start-page.jsp @@ -1,8 +1,8 @@ <%@ page contentType="text/html;charset=UTF-8" language="java" %> -<%@include file="head.jsp"%> +<%@include file="parts/header.jsp" %>

<%= "Hello World!" %>


List Users - +<%@include file="parts/footer.jsp" %> diff --git a/src/main/webapp/assets/css/styles.min.css b/src/main/webapp/assets/css/styles.min.css new file mode 100644 index 0000000..320a7ad --- /dev/null +++ b/src/main/webapp/assets/css/styles.min.css @@ -0,0 +1 @@ +.fit-cover{object-fit:cover}.bs-icon{--bs-icon-size:.75rem;display:flex;flex-shrink:0;justify-content:center;align-items:center;font-size:var(--bs-icon-size);width:calc(var(--bs-icon-size) * 2);height:calc(var(--bs-icon-size) * 2);color:var(--bs-primary)}.bs-icon-xs{--bs-icon-size:1rem;width:calc(var(--bs-icon-size) * 1.5);height:calc(var(--bs-icon-size) * 1.5)}.bs-icon-sm{--bs-icon-size:1rem}.bs-icon-md{--bs-icon-size:1.5rem}.bs-icon-lg{--bs-icon-size:2rem}.bs-icon-xl{--bs-icon-size:2.5rem}.bs-icon.bs-icon-primary{color:var(--bs-white);background:var(--bs-primary)}.bs-icon.bs-icon-primary-light{color:var(--bs-primary);background:rgba(var(--bs-primary-rgb),.2)}.bs-icon.bs-icon-semi-white{color:var(--bs-primary);background:rgba(255,255,255,.5)}.bs-icon.bs-icon-rounded{border-radius:.5rem}.bs-icon.bs-icon-circle{border-radius:50%} \ No newline at end of file From f8750ea6cd65df66085e71317b8440a9d03effe7 Mon Sep 17 00:00:00 2001 From: akhmelev Date: Mon, 12 Jan 2026 18:59:11 +0300 Subject: [PATCH 02/50] 3.0 REF Restore home (start page) --- pom.xml | 2 +- .../java/com/javarush/khmelov/cmd/Home.java | 6 +++++ .../khmelov/controller/HttpResolver.java | 2 +- src/main/webapp/WEB-INF/home.jsp | 8 ++++++ src/main/webapp/WEB-INF/parts/header.jsp | 26 +++++++++++++------ 5 files changed, 34 insertions(+), 10 deletions(-) create mode 100644 src/main/java/com/javarush/khmelov/cmd/Home.java create mode 100644 src/main/webapp/WEB-INF/home.jsp diff --git a/pom.xml b/pom.xml index 81a6d68..78ee59d 100644 --- a/pom.xml +++ b/pom.xml @@ -5,7 +5,7 @@ 4.0.0 com.javarush.khmelov - project-pantera + project-ledzeppelin 1.0-SNAPSHOT ProjectLedzeppelin war diff --git a/src/main/java/com/javarush/khmelov/cmd/Home.java b/src/main/java/com/javarush/khmelov/cmd/Home.java new file mode 100644 index 0000000..ac357b5 --- /dev/null +++ b/src/main/java/com/javarush/khmelov/cmd/Home.java @@ -0,0 +1,6 @@ +package com.javarush.khmelov.cmd; + +@SuppressWarnings("unused") +public class Home implements Command { + +} diff --git a/src/main/java/com/javarush/khmelov/controller/HttpResolver.java b/src/main/java/com/javarush/khmelov/controller/HttpResolver.java index 18bb761..f2f8f8e 100644 --- a/src/main/java/com/javarush/khmelov/controller/HttpResolver.java +++ b/src/main/java/com/javarush/khmelov/controller/HttpResolver.java @@ -10,7 +10,7 @@ public Command resolve(HttpServletRequest request) { // /cmd-example try { String requestURI = request.getRequestURI(); - requestURI = requestURI.equals("/") ? "/start-page" : requestURI; + requestURI = requestURI.equals("/") ? "/home" : requestURI; String kebabName = requestURI.split("[?#/]")[1]; String simpleName = convertKebabStyleToCamelCase(kebabName); String fullName = Command.class.getPackageName() + "." + simpleName; diff --git a/src/main/webapp/WEB-INF/home.jsp b/src/main/webapp/WEB-INF/home.jsp new file mode 100644 index 0000000..fee54be --- /dev/null +++ b/src/main/webapp/WEB-INF/home.jsp @@ -0,0 +1,8 @@ +<%@ page contentType="text/html;charset=UTF-8" language="java" %> +<%@include file="parts/header.jsp" %> + +

<%= "Hello World!" %> +

+
+List Users +<%@include file="parts/footer.jsp" %> diff --git a/src/main/webapp/WEB-INF/parts/header.jsp b/src/main/webapp/WEB-INF/parts/header.jsp index 2b68d57..f4ec9e0 100644 --- a/src/main/webapp/WEB-INF/parts/header.jsp +++ b/src/main/webapp/WEB-INF/parts/header.jsp @@ -15,17 +15,27 @@
From a09b8841b760f0960fc09842d72c9a5a72c02401 Mon Sep 17 00:00:00 2001 From: akhmelev Date: Mon, 12 Jan 2026 19:55:16 +0300 Subject: [PATCH 03/50] 3.0 ADD ImageService for edit-user --- README.MD | 28 ++++++ .../com/javarush/khmelov/cmd/EditUser.java | 23 ++--- .../khmelov/controller/FrontController.java | 2 + .../khmelov/controller/HttpResolver.java | 1 - .../khmelov/controller/ImageController.java | 29 ++++++ .../com/javarush/khmelov/entity/User.java | 2 +- .../khmelov/service/ImageService.java | 83 ++++++++++++++++++ src/main/webapp/WEB-INF/edit-user.jsp | 15 +++- src/main/webapp/WEB-INF/images/no-image.png | Bin 0 -> 12321 bytes src/main/webapp/WEB-INF/images/user-1.png | Bin 0 -> 5799 bytes src/main/webapp/WEB-INF/images/user-2.jpg | Bin 0 -> 42765 bytes src/main/webapp/WEB-INF/images/user-3.webp | Bin 0 -> 8420 bytes src/main/webapp/WEB-INF/list-user.jsp | 10 ++- src/main/webapp/img/blank-photo.png | Bin 0 -> 6361 bytes 14 files changed, 172 insertions(+), 21 deletions(-) create mode 100644 README.MD create mode 100644 src/main/java/com/javarush/khmelov/controller/ImageController.java create mode 100644 src/main/java/com/javarush/khmelov/service/ImageService.java create mode 100644 src/main/webapp/WEB-INF/images/no-image.png create mode 100644 src/main/webapp/WEB-INF/images/user-1.png create mode 100644 src/main/webapp/WEB-INF/images/user-2.jpg create mode 100644 src/main/webapp/WEB-INF/images/user-3.webp create mode 100644 src/main/webapp/img/blank-photo.png diff --git a/README.MD b/README.MD new file mode 100644 index 0000000..1bf1eca --- /dev/null +++ b/README.MD @@ -0,0 +1,28 @@ +# Quest Pantera + +### Ребята и девчата, всем привет! + +На этот раз у нас более сложный проект, который будет трудно объединить в один общий проект со всем зависимостями. +Поэтому поступим просто. Тут первоначально лежит наш учебный проект. +Заготовка, такая же как может быть сгенерирована в IDEA. +Договоримся, что Java у нас будет 21-я. У всех. Привыкайте использовать только LTS. +Из зависимостей есть jakarta.servlet-api + jstl, JUnit5 и Lombok, +так что POM.xml возможно надо будет заменить на свой. + +### Что нужно сделать. + +1. Сделайте форк и затем клон этого репозитория. +2. **СРАЗУ** создайте в нем ветку со своей фамилией, например, ivanov. +3. Размещайте в своей ветке свое решение (если мои примеры мешают их можно просто удалить - ветвление же). +4. В конце вам нужно будет сделать **Pull Request** из своей ветки (в своем форке) в такую же точно ветку (в этом удаленном + репозитории), если вы не найдете - тогда в **main** ну и затем ОБЯЗАТЕЛЬНО заполнить форму проектов. +5. Я положу в ветку **khmelov** тот пример, который буду параллельно с вами разрабатывать на факультативах. Полезнее будет + не копипастить, смотрите видео, разбирайтесь, что, почему, где и как сделано. Не копируйте, нужно ВАШЕ решение. Но мы уже и + не совсем зеленые, правда ведь ;). +6. Проверять проекты по итогам третьего модуля я буду с **code review**, и скорее всего быстро не получится, так что за + сроки сдачи можно не волноваться. +7. Планируйте время так, первые три консультации - основной функционал. Последняя консультация - плюшки, рефакторинг, + тесты, красоты. +8. Если будут какие-то моменты - я обновлю это README. Поглядывайте периодически. + +#### _2026 JRU Pantera, Александр Хмелев._ diff --git a/src/main/java/com/javarush/khmelov/cmd/EditUser.java b/src/main/java/com/javarush/khmelov/cmd/EditUser.java index ae191b4..1d91490 100644 --- a/src/main/java/com/javarush/khmelov/cmd/EditUser.java +++ b/src/main/java/com/javarush/khmelov/cmd/EditUser.java @@ -2,37 +2,33 @@ import com.javarush.khmelov.entity.Role; import com.javarush.khmelov.entity.User; +import com.javarush.khmelov.service.ImageService; import com.javarush.khmelov.service.UserService; import jakarta.servlet.http.HttpServletRequest; - -import java.util.Optional; +import lombok.AllArgsConstructor; +import lombok.SneakyThrows; @SuppressWarnings("unused") +@AllArgsConstructor public class EditUser implements Command { private final UserService userService; - - public EditUser(UserService userService) { - this.userService = userService; - } - + private final ImageService imageService; @Override public String doGet(HttpServletRequest req) { String stringId = req.getParameter("id"); if (stringId != null) { long id = Long.parseLong(stringId); - Optional optionalUser = userService.get(id); - if (optionalUser.isPresent()) { - User user = optionalUser.get(); - req.setAttribute("user", user); - } + userService.get(id) + .ifPresent(user -> req.setAttribute("user", user)); } return getView(); } @Override + @SneakyThrows public String doPost(HttpServletRequest req) { User user = User.builder() .login(req.getParameter("login")) @@ -45,8 +41,7 @@ public String doPost(HttpServletRequest req) { user.setId(Long.parseLong(req.getParameter("id"))); userService.update(user); } + imageService.uploadImage(req, user.getImage()); return getView() + "?id=" + user.getId(); } - - } \ No newline at end of file diff --git a/src/main/java/com/javarush/khmelov/controller/FrontController.java b/src/main/java/com/javarush/khmelov/controller/FrontController.java index 33242b2..f2ca678 100644 --- a/src/main/java/com/javarush/khmelov/controller/FrontController.java +++ b/src/main/java/com/javarush/khmelov/controller/FrontController.java @@ -5,6 +5,7 @@ import com.javarush.khmelov.entity.Role; import jakarta.servlet.ServletConfig; import jakarta.servlet.ServletException; +import jakarta.servlet.annotation.MultipartConfig; import jakarta.servlet.annotation.WebServlet; import jakarta.servlet.http.HttpServlet; import jakarta.servlet.http.HttpServletRequest; @@ -13,6 +14,7 @@ import java.io.IOException; @WebServlet({"", "/home", "/list-user", "/edit-user"}) +@MultipartConfig(fileSizeThreshold = 1 << 20) public class FrontController extends HttpServlet { private final HttpResolver httpResolver = Winter.find(HttpResolver.class); diff --git a/src/main/java/com/javarush/khmelov/controller/HttpResolver.java b/src/main/java/com/javarush/khmelov/controller/HttpResolver.java index f2f8f8e..58176dc 100644 --- a/src/main/java/com/javarush/khmelov/controller/HttpResolver.java +++ b/src/main/java/com/javarush/khmelov/controller/HttpResolver.java @@ -7,7 +7,6 @@ public class HttpResolver { public Command resolve(HttpServletRequest request) { - // /cmd-example try { String requestURI = request.getRequestURI(); requestURI = requestURI.equals("/") ? "/home" : requestURI; diff --git a/src/main/java/com/javarush/khmelov/controller/ImageController.java b/src/main/java/com/javarush/khmelov/controller/ImageController.java new file mode 100644 index 0000000..8da8719 --- /dev/null +++ b/src/main/java/com/javarush/khmelov/controller/ImageController.java @@ -0,0 +1,29 @@ +package com.javarush.khmelov.controller; + +import com.javarush.khmelov.config.Winter; +import com.javarush.khmelov.service.ImageService; +import jakarta.servlet.annotation.WebServlet; +import jakarta.servlet.http.HttpServlet; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import lombok.SneakyThrows; + +import java.nio.file.Files; +import java.nio.file.Path; + +@WebServlet("/images/*") +public class ImageController extends HttpServlet { + + + private final ImageService imageService = Winter.find(ImageService.class); + + @Override + @SneakyThrows + protected void doGet(HttpServletRequest req, HttpServletResponse resp) { + String requestURI = req.getRequestURI(); + String target = req.getContextPath() + "/images/"; + String nameImage = requestURI.replace(target, ""); + Path path = imageService.getImagePath(nameImage); + Files.copy(path, resp.getOutputStream()); + } +} diff --git a/src/main/java/com/javarush/khmelov/entity/User.java b/src/main/java/com/javarush/khmelov/entity/User.java index f7fa2d6..eba13f6 100644 --- a/src/main/java/com/javarush/khmelov/entity/User.java +++ b/src/main/java/com/javarush/khmelov/entity/User.java @@ -20,7 +20,7 @@ public class User { private Role role; public String getImage() { //TODO move to DTO - return "image-" + id; + return "user-" + id; } } diff --git a/src/main/java/com/javarush/khmelov/service/ImageService.java b/src/main/java/com/javarush/khmelov/service/ImageService.java new file mode 100644 index 0000000..7f76c53 --- /dev/null +++ b/src/main/java/com/javarush/khmelov/service/ImageService.java @@ -0,0 +1,83 @@ +package com.javarush.khmelov.service; + +import jakarta.servlet.ServletException; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.Part; +import lombok.SneakyThrows; + +import java.io.IOException; +import java.io.InputStream; +import java.net.URI; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.nio.file.StandardCopyOption; +import java.util.List; +import java.util.Objects; + +public class ImageService { + + private static final String IMAGES_FOLDER = "images"; + private static final String PART_NAME = "image"; + private static final String NO_IMAGE_PNG = "no-image.png"; + private static final List EXTENSIONS = List.of( + ".jpg", ".jpeg", ".png", ".bmp", ".gif", ".webp" + ); + + public final Path WEB_INF = Paths.get(URI.create( + Objects.requireNonNull( + ImageService.class.getResource("/") + ).toString())) + .getParent(); + + private final Path imagesFolder = WEB_INF.resolve(IMAGES_FOLDER); + + @SneakyThrows + public ImageService() { + Files.createDirectories(imagesFolder); + } + + + @SneakyThrows + public Path getImagePath(String filename) { + return EXTENSIONS.stream() + .map(ext -> imagesFolder.resolve(filename + ext)) + .filter(Files::exists) + .findAny() + .orElse(imagesFolder.resolve(NO_IMAGE_PNG)); + } + + public void uploadImage(HttpServletRequest req, String imageId) throws IOException, ServletException { + Part data = req.getPart(PART_NAME); + if (Objects.nonNull(data) && data.getInputStream().available() > 0) { + String filename = data.getSubmittedFileName(); + String ext = filename.substring(filename.lastIndexOf(".")); + deleteOldFiles(imageId); + filename = imageId + ext; + uploadImageInternal(filename, data.getInputStream()); + } + } + + private void deleteOldFiles(String filename) { + EXTENSIONS.stream() + .map(ext -> imagesFolder.resolve(filename + ext)) + .filter(Files::exists) + .forEach(p -> { + try { + Files.deleteIfExists(p); + } catch (IOException e) { + throw new RuntimeException(e); + } + }); + } + + @SneakyThrows + private void uploadImageInternal(String name, InputStream data) { + try (data) { + if (data.available() > 0) { + Files.copy(data, imagesFolder.resolve(name), StandardCopyOption.REPLACE_EXISTING); + } + } + } + +} diff --git a/src/main/webapp/WEB-INF/edit-user.jsp b/src/main/webapp/WEB-INF/edit-user.jsp index 01cca07..d0d3e9d 100644 --- a/src/main/webapp/WEB-INF/edit-user.jsp +++ b/src/main/webapp/WEB-INF/edit-user.jsp @@ -2,12 +2,23 @@ <%@include file="parts/header.jsp" %>
-
+
Edit user: - + +
+ +
diff --git a/src/main/webapp/WEB-INF/images/no-image.png b/src/main/webapp/WEB-INF/images/no-image.png new file mode 100644 index 0000000000000000000000000000000000000000..9fdc684039975f2341e6b7385e256ce4e67fd741 GIT binary patch literal 12321 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 x82nc5vaJCAcSLw3VE_dqAQ@?l|1L6EL@oWUDZ literal 0 HcmV?d00001 diff --git a/src/main/webapp/WEB-INF/images/user-1.png b/src/main/webapp/WEB-INF/images/user-1.png new file mode 100644 index 0000000000000000000000000000000000000000..e0e77ab91edb7421733e3d242cf79d97a98aa7b2 GIT binary patch literal 5799 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 <%@include file="parts/header.jsp" %> - - ${user.login} - +
+ + images/${user.image} + Edit user ${user.login}

+
+ +
<%@include file="parts/footer.jsp" %> diff --git a/src/main/webapp/img/blank-photo.png b/src/main/webapp/img/blank-photo.png new file mode 100644 index 0000000000000000000000000000000000000000..dc2bb4532fe614ca55cdc9b2e7d00c834067d06a GIT binary patch literal 6361 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 YjgTw8#jA$W02Anp Date: Mon, 12 Jan 2026 19:59:30 +0300 Subject: [PATCH 04/50] 3.0 ADD Signup, Login, Logout, Profile --- pom.xml | 4 +- .../com/javarush/khmelov/cmd/EditUser.java | 20 ++++---- .../com/javarush/khmelov/cmd/ListUser.java | 6 +-- .../java/com/javarush/khmelov/cmd/Login.java | 32 +++++++++++++ .../java/com/javarush/khmelov/cmd/Logout.java | 15 ++++++ .../com/javarush/khmelov/cmd/Profile.java | 21 +++++++++ .../java/com/javarush/khmelov/cmd/Signup.java | 35 ++++++++++++++ .../com/javarush/khmelov/config/Winter.java | 2 +- .../khmelov/controller/FrontController.java | 9 +++- .../khmelov/repository/UserRepository.java | 23 +++++++-- .../javarush/khmelov/service/UserService.java | 11 ++++- .../java/com/javarush/khmelov/util/Go.java | 19 ++++++++ .../java/com/javarush/khmelov/util/Key.java | 28 +++++++++++ src/main/webapp/WEB-INF/edit-user.jsp | 11 +++-- src/main/webapp/WEB-INF/login.jsp | 30 ++++++++++++ src/main/webapp/WEB-INF/parts/header.jsp | 18 +++++-- src/main/webapp/WEB-INF/profile.jsp | 27 +++++++++++ src/main/webapp/WEB-INF/signup.jsp | 44 ++++++++++++++++++ src/main/webapp/assets/img/blank-photo.png | Bin 0 -> 6361 bytes 19 files changed, 321 insertions(+), 34 deletions(-) create mode 100644 src/main/java/com/javarush/khmelov/cmd/Login.java create mode 100644 src/main/java/com/javarush/khmelov/cmd/Logout.java create mode 100644 src/main/java/com/javarush/khmelov/cmd/Profile.java create mode 100644 src/main/java/com/javarush/khmelov/cmd/Signup.java create mode 100644 src/main/java/com/javarush/khmelov/util/Go.java create mode 100644 src/main/java/com/javarush/khmelov/util/Key.java create mode 100644 src/main/webapp/WEB-INF/login.jsp create mode 100644 src/main/webapp/WEB-INF/profile.jsp create mode 100644 src/main/webapp/WEB-INF/signup.jsp create mode 100644 src/main/webapp/assets/img/blank-photo.png diff --git a/pom.xml b/pom.xml index 78ee59d..85247d0 100644 --- a/pom.xml +++ b/pom.xml @@ -5,9 +5,9 @@ 4.0.0 com.javarush.khmelov - project-ledzeppelin + project-pantera 1.0-SNAPSHOT - ProjectLedzeppelin + ProjectPantera war diff --git a/src/main/java/com/javarush/khmelov/cmd/EditUser.java b/src/main/java/com/javarush/khmelov/cmd/EditUser.java index 1d91490..f7f611e 100644 --- a/src/main/java/com/javarush/khmelov/cmd/EditUser.java +++ b/src/main/java/com/javarush/khmelov/cmd/EditUser.java @@ -4,6 +4,7 @@ import com.javarush.khmelov.entity.User; import com.javarush.khmelov.service.ImageService; import com.javarush.khmelov.service.UserService; +import com.javarush.khmelov.util.Key; import jakarta.servlet.http.HttpServletRequest; import lombok.AllArgsConstructor; import lombok.SneakyThrows; @@ -18,11 +19,11 @@ public class EditUser implements Command { @Override public String doGet(HttpServletRequest req) { - String stringId = req.getParameter("id"); + String stringId = req.getParameter(Key.ID); if (stringId != null) { long id = Long.parseLong(stringId); userService.get(id) - .ifPresent(user -> req.setAttribute("user", user)); + .ifPresent(user -> req.setAttribute(Key.USER, user)); } return getView(); } @@ -30,17 +31,14 @@ public String doGet(HttpServletRequest req) { @Override @SneakyThrows public String doPost(HttpServletRequest req) { + long id = Long.parseLong(req.getParameter(Key.ID)); User user = User.builder() - .login(req.getParameter("login")) - .password(req.getParameter("password")) - .role(Role.valueOf(req.getParameter("role"))) + .id(id) + .login(req.getParameter(Key.LOGIN)) + .password(req.getParameter(Key.PASSWORD)) + .role(Role.valueOf(req.getParameter(Key.ROLE))) .build(); - if (req.getParameter("create") != null) { - userService.create(user); - } else if (req.getParameter("update") != null) { - user.setId(Long.parseLong(req.getParameter("id"))); - userService.update(user); - } + userService.update(user); imageService.uploadImage(req, user.getImage()); return getView() + "?id=" + user.getId(); } diff --git a/src/main/java/com/javarush/khmelov/cmd/ListUser.java b/src/main/java/com/javarush/khmelov/cmd/ListUser.java index 9257917..f227e7a 100644 --- a/src/main/java/com/javarush/khmelov/cmd/ListUser.java +++ b/src/main/java/com/javarush/khmelov/cmd/ListUser.java @@ -3,18 +3,16 @@ import com.javarush.khmelov.entity.User; import com.javarush.khmelov.service.UserService; import jakarta.servlet.http.HttpServletRequest; +import lombok.AllArgsConstructor; import java.util.Collection; @SuppressWarnings("unused") +@AllArgsConstructor public class ListUser implements Command { private final UserService userService; - public ListUser(UserService userService) { - this.userService = userService; - } - @Override public String doGet(HttpServletRequest request) { Collection users = userService.getAll(); diff --git a/src/main/java/com/javarush/khmelov/cmd/Login.java b/src/main/java/com/javarush/khmelov/cmd/Login.java new file mode 100644 index 0000000..d2a2590 --- /dev/null +++ b/src/main/java/com/javarush/khmelov/cmd/Login.java @@ -0,0 +1,32 @@ +package com.javarush.khmelov.cmd; + +import com.javarush.khmelov.entity.User; +import com.javarush.khmelov.service.UserService; +import com.javarush.khmelov.util.Go; +import com.javarush.khmelov.util.Key; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpSession; +import lombok.AllArgsConstructor; + +import java.util.Optional; + +@SuppressWarnings("unused") +@AllArgsConstructor +public class Login implements Command { + + private final UserService userService; + + @Override + public String doPost(HttpServletRequest request) { + String login = request.getParameter(Key.LOGIN); + String password = request.getParameter(Key.PASSWORD); + Optional user = userService.get(login, password); + if (user.isPresent()) { + HttpSession session = request.getSession(); + session.setAttribute(Key.USER, user.get()); + return Go.PROFILE; + } else { + return Go.LOGIN; //todo add error message + } + } +} diff --git a/src/main/java/com/javarush/khmelov/cmd/Logout.java b/src/main/java/com/javarush/khmelov/cmd/Logout.java new file mode 100644 index 0000000..20c3515 --- /dev/null +++ b/src/main/java/com/javarush/khmelov/cmd/Logout.java @@ -0,0 +1,15 @@ +package com.javarush.khmelov.cmd; + +import com.javarush.khmelov.util.Go; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpSession; + +@SuppressWarnings("unused") +public class Logout implements Command { + @Override + public String doGet(HttpServletRequest request) { + HttpSession session = request.getSession(); + session.invalidate(); + return Go.LOGIN; + } +} diff --git a/src/main/java/com/javarush/khmelov/cmd/Profile.java b/src/main/java/com/javarush/khmelov/cmd/Profile.java new file mode 100644 index 0000000..f60b754 --- /dev/null +++ b/src/main/java/com/javarush/khmelov/cmd/Profile.java @@ -0,0 +1,21 @@ +package com.javarush.khmelov.cmd; + +import com.javarush.khmelov.entity.User; +import com.javarush.khmelov.util.Go; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpSession; + +@SuppressWarnings("unused") +public class Profile implements Command { + + @Override + public String doPost(HttpServletRequest request) { + if (request.getParameter("logout") == null) { + HttpSession session = request.getSession(); + User user = (User) session.getAttribute("user"); + return Go.EDIT_USER + "?id=" + user.getId(); + } else { + return Go.LOGOUT; + } + } +} diff --git a/src/main/java/com/javarush/khmelov/cmd/Signup.java b/src/main/java/com/javarush/khmelov/cmd/Signup.java new file mode 100644 index 0000000..55e94ee --- /dev/null +++ b/src/main/java/com/javarush/khmelov/cmd/Signup.java @@ -0,0 +1,35 @@ +package com.javarush.khmelov.cmd; + +import com.javarush.khmelov.entity.Role; +import com.javarush.khmelov.entity.User; +import com.javarush.khmelov.service.ImageService; +import com.javarush.khmelov.service.UserService; +import com.javarush.khmelov.util.Go; +import com.javarush.khmelov.util.Key; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpSession; +import lombok.AllArgsConstructor; +import lombok.SneakyThrows; + +@SuppressWarnings("unused") +@AllArgsConstructor +public class Signup implements Command { + + private final UserService userService; + private final ImageService imageService; + + @Override + @SneakyThrows + public String doPost(HttpServletRequest request) { + User user = User.builder() + .login(request.getParameter(Key.LOGIN)) + .password(request.getParameter(Key.PASSWORD)) + .role(Role.USER) + .build(); + userService.create(user); + imageService.uploadImage(request, user.getImage()); + HttpSession session = request.getSession(); + session.setAttribute(Key.USER, user); + return Go.PROFILE; + } +} diff --git a/src/main/java/com/javarush/khmelov/config/Winter.java b/src/main/java/com/javarush/khmelov/config/Winter.java index 48bd8a7..61c1a76 100644 --- a/src/main/java/com/javarush/khmelov/config/Winter.java +++ b/src/main/java/com/javarush/khmelov/config/Winter.java @@ -7,7 +7,7 @@ public class Winter { - public static ConcurrentHashMap, Object> components = new ConcurrentHashMap<>(); + public final static ConcurrentHashMap, Object> components = new ConcurrentHashMap<>(); @SuppressWarnings("unchecked") diff --git a/src/main/java/com/javarush/khmelov/controller/FrontController.java b/src/main/java/com/javarush/khmelov/controller/FrontController.java index f2ca678..d4f98dd 100644 --- a/src/main/java/com/javarush/khmelov/controller/FrontController.java +++ b/src/main/java/com/javarush/khmelov/controller/FrontController.java @@ -3,6 +3,7 @@ import com.javarush.khmelov.cmd.Command; import com.javarush.khmelov.config.Winter; import com.javarush.khmelov.entity.Role; +import com.javarush.khmelov.util.Go; import jakarta.servlet.ServletConfig; import jakarta.servlet.ServletException; import jakarta.servlet.annotation.MultipartConfig; @@ -13,8 +14,12 @@ import java.io.IOException; -@WebServlet({"", "/home", "/list-user", "/edit-user"}) @MultipartConfig(fileSizeThreshold = 1 << 20) +@WebServlet({ + Go.INDEX, Go.HOME, + Go.SIGNUP, Go.LOGIN, Go.LOGOUT, + Go.LIST_USER, Go.PROFILE, Go.EDIT_USER, +}) public class FrontController extends HttpServlet { private final HttpResolver httpResolver = Winter.find(HttpResolver.class); @@ -37,7 +42,7 @@ private static String getJsp(String view) { } @Override - protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { + protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws IOException { Command command = httpResolver.resolve(req); String redirect = command.doPost(req); resp.sendRedirect(redirect); diff --git a/src/main/java/com/javarush/khmelov/repository/UserRepository.java b/src/main/java/com/javarush/khmelov/repository/UserRepository.java index 58b32ea..b107845 100644 --- a/src/main/java/com/javarush/khmelov/repository/UserRepository.java +++ b/src/main/java/com/javarush/khmelov/repository/UserRepository.java @@ -8,17 +8,17 @@ import java.util.Map; import java.util.Optional; import java.util.concurrent.atomic.AtomicLong; +import java.util.stream.Stream; public class UserRepository implements Repository { - private final Map map = new HashMap<>(); - public static final AtomicLong id = new AtomicLong(System.currentTimeMillis()); + private final Map map = new HashMap<>(); public UserRepository() { - map.put(1L, new User(1L, "Alisa", "qwerty", Role.USER)); - map.put(2L, new User(2L, "Bob", "", Role.GUEST)); - map.put(3L, new User(3L, "Carl", "admin", Role.ADMIN)); + map.put(1L, new User(1L, "Carl", "admin", Role.ADMIN)); + map.put(2L, new User(2L, "Alisa", "qwerty", Role.USER)); + map.put(3L, new User(3L, "Bob", "", Role.GUEST)); map.put(4L, new User(4L, "Khmelov", "admin", Role.ADMIN)); } @@ -47,4 +47,17 @@ public void update(User entity) { public void delete(User entity) { map.remove(entity.getId()); } + + public Stream find(User pattern) { + return map.values() + .stream() + .filter(u -> nullOrEquals(pattern.getId(), u.getId())) + .filter(u -> nullOrEquals(pattern.getLogin(), u.getLogin())) + .filter(u -> nullOrEquals(pattern.getPassword(), u.getPassword())) + .filter(u -> nullOrEquals(pattern.getRole(), u.getRole())); + } + + protected boolean nullOrEquals(Object patternField, Object repoField) { + return patternField == null || patternField.equals(repoField); + } } diff --git a/src/main/java/com/javarush/khmelov/service/UserService.java b/src/main/java/com/javarush/khmelov/service/UserService.java index 32011dc..5f3668d 100644 --- a/src/main/java/com/javarush/khmelov/service/UserService.java +++ b/src/main/java/com/javarush/khmelov/service/UserService.java @@ -1,7 +1,7 @@ package com.javarush.khmelov.service; -import com.javarush.khmelov.repository.UserRepository; import com.javarush.khmelov.entity.User; +import com.javarush.khmelov.repository.UserRepository; import java.util.Collection; import java.util.Optional; @@ -33,4 +33,13 @@ public Collection getAll() { public Optional get(long id) { return userRepository.get(id); } + + public Optional get(String login, String password) { + User patternUser = User + .builder() + .login(login) + .password(password) + .build(); + return userRepository.find(patternUser).findAny(); + } } diff --git a/src/main/java/com/javarush/khmelov/util/Go.java b/src/main/java/com/javarush/khmelov/util/Go.java new file mode 100644 index 0000000..dd091b6 --- /dev/null +++ b/src/main/java/com/javarush/khmelov/util/Go.java @@ -0,0 +1,19 @@ +package com.javarush.khmelov.util; + +@SuppressWarnings("unused") +public class Go { + public static final String INDEX = ""; + public static final String HOME = "/home"; + + public static final String SIGNUP = "/signup"; + public static final String LOGIN = "/login"; + public static final String LOGOUT = "/logout"; + public static final String PROFILE = "/profile"; + public static final String LIST_USER = "/list-user"; + public static final String EDIT_USER = "/edit-user"; + + public static final String CREATE = "/create-quest"; + public static final String QUEST = "/quest"; + public static final String STAT = "/stat"; + public static final String GAME = "/game"; +} diff --git a/src/main/java/com/javarush/khmelov/util/Key.java b/src/main/java/com/javarush/khmelov/util/Key.java new file mode 100644 index 0000000..5855fdf --- /dev/null +++ b/src/main/java/com/javarush/khmelov/util/Key.java @@ -0,0 +1,28 @@ +package com.javarush.khmelov.util; + +@SuppressWarnings("unused") +public class Key { + public static final String INDEX = "index"; + + public static final String ID = "id"; + public static final String SIGNUP = "signup"; + public static final String LOGIN = "login"; + public static final String PASSWORD = "password"; + public static final String ROLE = "role"; + public static final String ROLES = "roles"; + public static final String USERS = "users"; + public static final String USER = "user"; + + public static final String CREATE_QUEST = "create-quest"; + public static final String QUESTS = "quests"; + public static final String QUEST_ID = "questId"; + public static final String QUEST = "quest"; + public static final String QUESTION = "question"; + + public static final String NAME = "name"; + public static final String TEXT = "text"; + + public static final String ERROR_MESSAGE = "errorMessage"; + public static final String GAME = "game"; + public static final String ANSWER = "answer"; +} diff --git a/src/main/webapp/WEB-INF/edit-user.jsp b/src/main/webapp/WEB-INF/edit-user.jsp index d0d3e9d..3a7775e 100644 --- a/src/main/webapp/WEB-INF/edit-user.jsp +++ b/src/main/webapp/WEB-INF/edit-user.jsp @@ -14,9 +14,12 @@ src="images/${requestScope.user.image}" width="150" alt="${requestScope.user.image}"> - +

Hint: Это версия без JS +
Нет мгновенного обновления картинки +
Удобнее сделано в signup.jsp +

@@ -46,7 +49,7 @@ placeholder="your password" class="form-control input-md" required=""> - min 8 symb + min 8 symbols
diff --git a/src/main/webapp/WEB-INF/login.jsp b/src/main/webapp/WEB-INF/login.jsp new file mode 100644 index 0000000..7a79c62 --- /dev/null +++ b/src/main/webapp/WEB-INF/login.jsp @@ -0,0 +1,30 @@ +<%@ page contentType="text/html;charset=UTF-8" language="java" %> +<%@include file="parts/header.jsp" %> +
+
+
+
+

Login

+

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

+
+
+
+
+
+
+
+ +
+ +
+
+
+

Forgot your password?

+ +
+
+
+
+
+
+<%@include file="parts/footer.jsp" %> diff --git a/src/main/webapp/WEB-INF/parts/header.jsp b/src/main/webapp/WEB-INF/parts/header.jsp index f4ec9e0..158472a 100644 --- a/src/main/webapp/WEB-INF/parts/header.jsp +++ b/src/main/webapp/WEB-INF/parts/header.jsp @@ -6,7 +6,7 @@ - Gorillaz + Pantera @@ -22,7 +22,7 @@ - Gorillaz + Pantera diff --git a/src/main/webapp/WEB-INF/profile.jsp b/src/main/webapp/WEB-INF/profile.jsp new file mode 100644 index 0000000..b119554 --- /dev/null +++ b/src/main/webapp/WEB-INF/profile.jsp @@ -0,0 +1,27 @@ +<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> +<%@ page contentType="text/html;charset=UTF-8" %> + +
+ + +
+

+

+ ${user.image} +
+

User login: ${user.login}

+

User role: ${user.role}

+
+
+
+ + + +
+
+
+
+
+ + diff --git a/src/main/webapp/WEB-INF/signup.jsp b/src/main/webapp/WEB-INF/signup.jsp new file mode 100644 index 0000000..709fe4f --- /dev/null +++ b/src/main/webapp/WEB-INF/signup.jsp @@ -0,0 +1,44 @@ +<%@ page contentType="text/html;charset=UTF-8" %> +<%@include file="parts/header.jsp" %> +
+
+
+
+
+
+
+

Signup

+
+

Нажмите для загрузки фото

+ +
+ + + +
+

Укажите данные для регистрации

+
+
+
+
+
+
+
+
+
+
+
+<%@include file="parts/footer.jsp" %> diff --git a/src/main/webapp/assets/img/blank-photo.png b/src/main/webapp/assets/img/blank-photo.png new file mode 100644 index 0000000000000000000000000000000000000000..dc2bb4532fe614ca55cdc9b2e7d00c834067d06a GIT binary patch literal 6361 zcmZ`-c|4R~xSz#Xh7rQZWJwBRjTnXO63K)aGnNc8WG_p|GPbfLijk%4Sx1c}KUqhV zLZKQ)8m9bYn`CKGiTk?ubMO7!Kkn!Kyyt!2=Y5uQp6C0V^PH28J$D)bmxhBtAjBDK z3kMJgtP0$M{Ja1qk-sbnc<_4Jp0)t(?p-|1_Fuw*}K76#YXFU#K=%n z-~W~e8E&n$f{rN^BRl){9o=YOcM0v1WKd&1V0(q3zLT`>v&UkBKw5=c6&A{GT^F zKb<+SY;iBj>@~hn$?H)GW_I$d?|MOe0AdpLs(WdtJn>g)-rti;F_&>GOq}k(OAOO{ zp`u>N=Y3^@eN3gUkQ@KO*IV)}Il0FNUe@F~C_Qj;4Up1@>0h6EkQz4Lu-`)I!3)wE z$`7rv*FE7CnQ6|IkH^$SL<>{%lc;F}WmM#VrG=p*`G~@KEM4rjaxF?(;k=(}i`Z?c zz1P(vd+lxP|NK`bq+5$S0l$@%_ZmMYOf)@7TNP;h@d{!vyMIJQe?GUOXQoBvSCEb< zh)N0K>xt=UzC$z%ML2wqB?4-x2>;(tn5yVyP4LNDz=EiC;zZ~o-ufFktjX3K<4{pe zI^upL^-*0F!M`Noaf|U=fzd~HTYm;h=O0lM(!(oBN4z)WA#_9(jKyQf7YJ&DR!&V? zfBbX447b&Dzxy@zhc5G{Im483j4@4_XUuQ~q7*FQc&NrctPDwKO?eEoI@&Xv0;|5w zJw|hD>dH%KpYi*=yW_<%;lweP80m~1<>@=?&y%QdZt{`QvI~5&e8GGZpls3sry5$> zaVqz_Bk5}Qc&u!@*p_f8Bvg7WfZ@q-P!n`~uFJ|JA_@$xyZJvrV&&TRvCQNk@0{Sf zt8ForPk7t+4?n!s-;t&SQiAeTmg&}>R3;!&48!7u+rb2wAKaGCJ7QYcbbr*2A_r?HWc|6M%vRDMumM{2Uk+aPp3uc1J!s6L-+$>{^N z8F0&ArKBk)I2sv3!?XKu@W+@3Tdz>}PJDghT)1=qb^6lLDxDWkv&@NBBPJFit zQwkN3tToO?XyLBojJz8(th@ZVD*7J%6rOsw9|YB@Lt1kFyzROiC%VwEww0uI-dAD%Btr zsd%Gd};{iE4l?oUz8y{dlC8Z7gPOLjNS1>40tSCE0!6e$2WhWlJr zD82o+3avT^ZGj%WqsT$-f-B)pEx9r^NV+EiS>9LKk`Ltro9s4n7%vcjo@u`gn(hUgnsl=_1=s>N;Q@fY3+Ga7LGv+Hy)YSUXF>W!~z6uf9c;qZ}-WYqPv=_y5)jwuQ_+ zCti{XF!pDiJN{2%#V0jQa&Eg;iw)+^vZJj zTK=@jJ((TBw4BytBf|Z8qO`&f^S*e?VArZH7JlYEnU_tNmFhT@EI<6vs>vYvaQXCM z$2V+~8)77#HgM9nQ7GZBjk{^WhA0e~@Jm(E;Zr7MORY#uBNy16FCfY4N{X+G} z3rA4yj@5pTN0}n|N1a-()MiDTsj`P7Q=P|o>n>cv)|$(3t8VX1XCzUbgRrLK#p0+A zd4+4rgcI*f`_SU|1>+wxX5=HFf3akVTd!fgdBhX%Ad%OD2Pkiza2#z8wTQ`yk&t?S z&41f3B-yK^j@Z;alIt8h_!t{5yHzzZbjSzqx%BjlBOjCI_C8Cy0ejsHW76S3p2sHn z;J%C@4q%7sW2K#2#Eeh+8Es?VA--L5BR{~r$QCZz5Y5o|u%lRuTEu4ng5erlRe=AN zd9PQ7^(pz_enEAOP)&t2D8L~H=b9vc4f$C*N^Bbt6=kAq3O=YXM)E@zQ<8ehXo zkB9fEz#2luX|a38PgPs=)$jTUbfT$%9e_@#8I2&35XKjR5# z`kx0QLn^R(tvuqd2c3z+OXr;QJ5YY%Y$~F;<;3X^c}hJ7mh|kvDnV4bAGnna#$I zt-?J>0l?|bxofiEV7?3B3FzT+)NzVc;!!$`#iuXA5{wWul4>9V&T{Nedb>cm2F5=m z$adHzFB3UBgsY(fbTJ@>G~tAD6~YNDbd_%v3aiI9&z*t?QkvMoZD2XRnZzWV9{{s4 zupT4`hQ1mJHNYB8CO1JkpA%Exg7t0$r-U<)y5?IXIg;m1;9Zg(qHm~Ht$)f*%g-%t z{W*RU;e+i)8v-Fy6J~>!{H3}v7j6#IoLb=6 zF4vzEqssxOR}nB+HXIO zNPI0G9L~*tjnI5gvi-G)5LmHe-B()o7Ns8#Kg>fH{M{a02IqSITgdb*M=8v8fm!=R_t4laRCgL=jGDEzG}xkvz(EtocY_lk!k{i!Ao zo~{G;k+O=)EmxKlIcsBCBKgqXl(y`jW`$H884i;2)@1lu3ReGfZ*l}wf))$RZDR_Y zO4fZ-fjI)_aBcf)y0+?#lMGFWFQvI5e(2Ws3ydko^m+U|K(Dz@G46Y~_J(hNM}luk zaQ4}Xk)b+a$=XxALMolh#s5GSNV!A0BJYqs_}70$*V@LiYCE`ISm=&Wl+yA}^gCdP zt}1%$xvQp+8&2s<>dU7fSTx%-I;U&P-VgA})$l8+J3?8{$<#6e0KO$jRPT#L8{8f6|sG=a;>vGFik76@t_mBb) z6~JU9pT^`N07>7`y8!2SpFjS6(-XHrFR(2@aS@+Uf~od8aDzGNz_I{KefTYeR2-g` z5F}2kXp%B`B3T!V%Sfir)s4m-1jO$^lt8C%03AG-y^K3{9;2lo6L^DTY}Ih%wQJ-D zvB0JKap$|ylE6@MLB3uM#G&nOnW%(Cs~7iL$JmV8e>ZtXd1q?5{=v<2x#dxqNx~uKPEEt{>nJeciC6SuWK;z}YW(COPWzM(=`_bYE%y|s)teOJQp2(I z<)fAunffxswHO2(f7y}DWcw}hzDRmdiz2AN@$9V5OvUpyK!NBWPU}Ef_Msz(Xy|Ha zAcD<*BaisZz7Y4Um4e*UIq}rP4B|+}9TK1mMx6*ZKj?tbikHbF;;bNnR%}M_JH*XW z?FM)qI2(jA#|)Ia1I3e?scyp}v-y$Un%3GZL&NTI;lG!-8g-{;*NgIqm)NE4hArgm4gbKG&l_SIglTaB(jSs2@qMnlS9=VuK!goqSlUZ23e7AUtvGY z-1`fZTQaZ+KadB{go^bmH3O^4?^c_qfx_04kh^&cV`5KA3a#P1vKo~D~zqrqHWg}>9bq+7daq+qnLy~jiTVZypc0-yX z%c*GEgoqQc0^ocUK&}H+7CNzq@v4iqCbr@LIIEzBpum0X9F|1$8+E@CoEkv8h|$|} zbZ2Y?nstJoRU~USC@V5-8T)}GItIiJs5R21Ba7VwaMTq6PDJaboek5J#hR zp~5%lsMjnyi{0O8uAd^4 z5Fz-t0Epv*F}fI6aVovhHA1CLaU`vS(w(awX9#x_GK9v0^db7<1PC{|8&h;z>RhJ4 zphq82h(t13Lfhh7vTckR+cFoN@kMX|UnpYDouopSlWd(yZk&!+lWaffcjDi8N3sO= zSY`~oU1qpK780p!^4;TO!Pk+V3p7koHsl{*M(h=c71_d(4h*&ZKGRn}x&dN7Z?5ia za`%r<%Bp;$q5E1q-!#5_#xGuQ3%oW=a9(%fD3gv2wZ#NEB)a$2V>VF8Y5FVckp71g z_7DS!vQIhP_R}j_pV=&7^I6b2-gvnv%tQo_kE@R@mdtdFmeftky43@nt|F;NW*Xvh zG8|9oHO%-u?ANn*M|275Bev8z)+AAv`0BH0`cIP*%Wn9#Q2T*t-DsmR2TJ{|r`LYP zbdS3wyXVZnyX3Y+LJiR`?phY&&TOdpW7dx2)p|vvL|FA8TwH44$|X)OC7?-dMF`mV z0VQ^InXWM$FNhVw7AO9pdqvBzK&=v0y!IcBW(y?#4SBjMTLSLdr_T>-{Dn=7ll3RK z>?-J$1|lZd*LV>pa+0_$Yk=)8SZA8dzIy`OvY}GazqODy*h2mUqnM}=xePA=SF4c+M|RtmfGs1z4+ z{OjO#x?5_*KKnuVX7q$cu1ZUh*`2tczST9_>zxmfq8$an+_-obAV)m|)f)ros~-zl z-(<0N0Sd4NfsGR}n%)QA`KwjF7lu1HJbWrMFqo+69VRyVwEA1ENbl(+I2YLDe>c>- z@1?7EIs~I^eI~>ww|1I^y(`$H`*HNC)u5)isbC&43s&dbL>0z+!om?Vc|AkMA;3P9 zr+t5C|8_7P4np9219G3 zw5;2|W%cNen?Ej=NnAJKGVrlmD)`Zp`-@HD^w%%Cs1Pyqs0)K)w2jVd*-&S1_a20@ zb^1*mPyPXyz^h+z45g;Wb3X@nWd`#2fuY~{;M=r1!(_>bM*OrO=h&@W@~oVb>+49D zd)6jpAb+i3FtOUZ5O;f$M17RBrai&zgxF<@7PgrE3`MpLzj0^D4D2JB%iZ!Co$$I~ zkxQIkIm6Bfiffe&s?lhcPu=uA?HSNy+Z3IakO8B|=3Mey)oJL8>XB?0axHYUeVmp@ z3*}oU1_m@0(=EZqiRy5v4w0{VEkm8LD^2qrk02L7FqPXE(BYyO5Yl^p-7b8%ZR14R zq4p8l-uZ?`!~wlv8%J-8-^!{3SLtTk(FD;QM^@~B2zVGrrq0e^17$86n@rbd@;JU| zZX^AXpAo#5sm`x|cDEz-7g;)2yW#x@kAOaZ#tOmX*IQN&4-75J*StxGet>QxB-z{zl)NQjl}P&E=L*4Z3<6Mu-r}o zDh8W3!<5z{I1uhpg0Yv?F!ig*>)NFq^|t*^n%<{pbs7vSe~anecBmY)RKV$9o%DKn zTC+1H?iEce@Y}Cm@7!$NNE^i~sbTHH+kE&@o6mof99II!RIkJ1ntWCgwHEc|p&AJ& zL6`2dLvh12e-@=Vb9_tXc6|(DMK`E&n_I70h=T<3>`1sK+gPBhb12(V4W3k))4%T! zBPbydPu%O_K~(PXAbo8E6~ZJCC{rPR$odfYBeo&0aDr~=Ye-W|S( zT?^iq!>khi!$*3^_Q^PetH$>~Wvitxztru?++Kow(#Mb7vNEH%?j<15omH&qja_V?J= z{9-%xmTqlMbp;c%3zuh0M#DN~`j>8nO!W$`hy18$^pP1ZNI1pb#S||542!z}H-rZA z{_y_%GwkG+_#HZeJ86U;JwBn@d>2hw$*e0%MuZsml@ft|w|r?wn$m(bJU(y|U8{ zeSxyF2JB68*g^8|u=+`t7h%XZB^SD$JN2q=?snz;`1Xl4`b}ErZ(!S~`pRS9Klir3 zx(jl5_id%!xh^m%mJOW#9|^Z4gkSUw_c`f(#RqtRw6%_Dt83}1>l|}BcH*Rt_DO9$ zbuG+EEv=XupYs1B5EOFp5;^++7ZCi-mH`9~>{)OOA%|b}yy648diAQtrQm=tZ_iL4 YjgTw8#jA$W02Anp Date: Thu, 15 Jan 2026 18:49:25 +0300 Subject: [PATCH 05/50] 3.1 ADD JS-dummy and CreateQuest.java --- .../com/javarush/khmelov/cmd/CreateQuest.java | 21 +++++++++ .../khmelov/controller/FrontController.java | 1 + .../javarush/khmelov/util/RequestHelpers.java | 36 ++++++++++++++++ src/main/webapp/WEB-INF/create-quest.jsp | 43 +++++++++++++++++++ src/main/webapp/WEB-INF/parts/footer.jsp | 2 +- src/main/webapp/WEB-INF/parts/header.jsp | 2 +- src/main/webapp/WEB-INF/parts/quest-demo.jsp | 24 +++++++++++ src/main/webapp/WEB-INF/parts/quest-jru.txt | 16 +++++++ 8 files changed, 143 insertions(+), 2 deletions(-) create mode 100644 src/main/java/com/javarush/khmelov/cmd/CreateQuest.java create mode 100644 src/main/java/com/javarush/khmelov/util/RequestHelpers.java create mode 100644 src/main/webapp/WEB-INF/create-quest.jsp create mode 100644 src/main/webapp/WEB-INF/parts/quest-demo.jsp create mode 100644 src/main/webapp/WEB-INF/parts/quest-jru.txt diff --git a/src/main/java/com/javarush/khmelov/cmd/CreateQuest.java b/src/main/java/com/javarush/khmelov/cmd/CreateQuest.java new file mode 100644 index 0000000..ede2000 --- /dev/null +++ b/src/main/java/com/javarush/khmelov/cmd/CreateQuest.java @@ -0,0 +1,21 @@ +package com.javarush.khmelov.cmd; + +import com.javarush.khmelov.entity.User; +import com.javarush.khmelov.util.Go; +import com.javarush.khmelov.util.Key; +import com.javarush.khmelov.util.RequestHelpers; +import jakarta.servlet.http.HttpServletRequest; + +import java.util.Optional; + +public class CreateQuest implements Command { + + @Override + public String doPost(HttpServletRequest request) { + String name = request.getParameter(Key.NAME); + String text = request.getParameter(Key.TEXT); + Optional optionalUser = RequestHelpers.getUser(request.getSession()); + //optionalUser.ifPresent(user -> questService.create(name, text, user.getId())); + return Go.HOME; + } +} diff --git a/src/main/java/com/javarush/khmelov/controller/FrontController.java b/src/main/java/com/javarush/khmelov/controller/FrontController.java index d4f98dd..75a2de2 100644 --- a/src/main/java/com/javarush/khmelov/controller/FrontController.java +++ b/src/main/java/com/javarush/khmelov/controller/FrontController.java @@ -19,6 +19,7 @@ Go.INDEX, Go.HOME, Go.SIGNUP, Go.LOGIN, Go.LOGOUT, Go.LIST_USER, Go.PROFILE, Go.EDIT_USER, + Go.CREATE }) public class FrontController extends HttpServlet { diff --git a/src/main/java/com/javarush/khmelov/util/RequestHelpers.java b/src/main/java/com/javarush/khmelov/util/RequestHelpers.java new file mode 100644 index 0000000..ea9b8b5 --- /dev/null +++ b/src/main/java/com/javarush/khmelov/util/RequestHelpers.java @@ -0,0 +1,36 @@ +package com.javarush.khmelov.util; + +import com.javarush.khmelov.entity.User; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpSession; +import lombok.experimental.UtilityClass; + +import java.util.Optional; + +@UtilityClass +public class RequestHelpers { + + public Long getId(HttpServletRequest req) { + return getId(req, Key.ID); + } + + public Long getId(HttpServletRequest req, String key) { + String id = req.getParameter(key); + return id != null && !id.isBlank() + ? Long.parseLong(id) + : 0L; + } + + public Long getId(HttpSession session) { + Object user = session.getAttribute(Key.USER); + return user != null + ? ((User) user).getId() + : 0L; + } + + public Optional getUser(HttpSession session) { + return Optional + .ofNullable(session.getAttribute(Key.USER)) + .map(User.class::cast); // equivalent to .map(u -> (User) u); + } +} diff --git a/src/main/webapp/WEB-INF/create-quest.jsp b/src/main/webapp/WEB-INF/create-quest.jsp new file mode 100644 index 0000000..4edb75e --- /dev/null +++ b/src/main/webapp/WEB-INF/create-quest.jsp @@ -0,0 +1,43 @@ +<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> +<%@ page contentType="text/html;charset=UTF-8" %> + +
+
+ +
+ + +
+ +
+ + +
+ +
+ +
+ + +
+
+
+
+ + + + diff --git a/src/main/webapp/WEB-INF/parts/footer.jsp b/src/main/webapp/WEB-INF/parts/footer.jsp index d7b1f31..503f097 100644 --- a/src/main/webapp/WEB-INF/parts/footer.jsp +++ b/src/main/webapp/WEB-INF/parts/footer.jsp @@ -18,7 +18,7 @@ -

Copyright   © 2024 JRU Company Inc., Pantera Group. 

+

Copyright   © 2024 JRU Company, Inc. Pantera Group. 

diff --git a/src/main/webapp/WEB-INF/parts/header.jsp b/src/main/webapp/WEB-INF/parts/header.jsp index 158472a..8d92f4e 100644 --- a/src/main/webapp/WEB-INF/parts/header.jsp +++ b/src/main/webapp/WEB-INF/parts/header.jsp @@ -31,7 +31,7 @@ - @@ -63,6 +74,9 @@ Sign-up +
  • +

    ${sessionScope.user.login}

    +
  • From 7b3928d7e68fb6287cbdcd2c59aed8163f6f4e03 Mon Sep 17 00:00:00 2001 From: GDGo <50753433+GDGo@users.noreply.github.com> Date: Mon, 2 Feb 2026 00:13:57 +0300 Subject: [PATCH 34/50] edit logic delete and completed arrive messages --- ...eteMessage.java => CompletedMessages.java} | 30 ++++++----- .../goncharov/controller/Messages.java | 14 ++++-- .../repository/MessageRepository.java | 2 +- .../webapp/WEB-INF/completed-messages.jsp | 50 +++++++++++++++++++ src/main/webapp/WEB-INF/delete-message.jsp | 24 --------- src/main/webapp/WEB-INF/messages.jsp | 22 +++++--- 6 files changed, 96 insertions(+), 46 deletions(-) rename src/main/java/com/javarush/goncharov/controller/{DeleteMessage.java => CompletedMessages.java} (60%) create mode 100644 src/main/webapp/WEB-INF/completed-messages.jsp delete mode 100644 src/main/webapp/WEB-INF/delete-message.jsp diff --git a/src/main/java/com/javarush/goncharov/controller/DeleteMessage.java b/src/main/java/com/javarush/goncharov/controller/CompletedMessages.java similarity index 60% rename from src/main/java/com/javarush/goncharov/controller/DeleteMessage.java rename to src/main/java/com/javarush/goncharov/controller/CompletedMessages.java index f0f2efc..e74ffd4 100644 --- a/src/main/java/com/javarush/goncharov/controller/DeleteMessage.java +++ b/src/main/java/com/javarush/goncharov/controller/CompletedMessages.java @@ -1,6 +1,7 @@ package com.javarush.goncharov.controller; import com.javarush.goncharov.model.Message; +import com.javarush.goncharov.model.User; import com.javarush.goncharov.repository.MessageRepository; import com.javarush.goncharov.repository.Storage; import com.javarush.goncharov.service.MessageService; @@ -9,29 +10,34 @@ import jakarta.servlet.http.HttpServlet; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; +import jakarta.servlet.http.HttpSession; import java.io.IOException; +import java.util.Collection; import java.util.Optional; -@WebServlet("/delete-message") -public class DeleteMessage extends HttpServlet { +@WebServlet("/completed-messages") +public class CompletedMessages extends HttpServlet { private final Storage messageStorage = Storage.getInstance(); private final MessageService messageService = new MessageService(new MessageRepository(messageStorage)); @Override - protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { - Long idMessage = Long.parseLong(req.getParameter("id")); - Optional userFind = messageService.get(idMessage); - if (req.getParameter("action").equals("delete")) { - userFind.ifPresent(messageService::delete); - } - resp.sendRedirect("/messages"); + protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { + Collection messages = messageService.getAll().values(); + HttpSession session = req.getSession(); + User user = (User) session.getAttribute("userSession"); + req.setAttribute("messages", messages); + req.setAttribute("user", user); + req.getRequestDispatcher("/WEB-INF/completed-messages.jsp").forward(req, resp); } @Override - protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { + protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { Long idMessage = Long.parseLong(req.getParameter("id")); - messageService.get(idMessage).ifPresent(message -> req.setAttribute("message", message)); - req.getRequestDispatcher("/WEB-INF/delete-message.jsp").forward(req, resp); + Optional message = messageService.get(idMessage); + if (req.getParameter("action").equals("delete")) { + messageService.delete(message.get()); + } + resp.sendRedirect("/completed-messages"); } } diff --git a/src/main/java/com/javarush/goncharov/controller/Messages.java b/src/main/java/com/javarush/goncharov/controller/Messages.java index 55f1d11..3bb4b1e 100644 --- a/src/main/java/com/javarush/goncharov/controller/Messages.java +++ b/src/main/java/com/javarush/goncharov/controller/Messages.java @@ -28,17 +28,25 @@ protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws Se User user = (User) session.getAttribute("userSession"); req.setAttribute("messages", messages); req.setAttribute("user", user); - System.out.println(messages); req.getRequestDispatcher("/WEB-INF/messages.jsp").forward(req, resp); } @Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { + if (req.getParameter("action").equals("completedValues")) { + resp.sendRedirect("/completed-messages"); + return; + } + Long idMessage = Long.parseLong(req.getParameter("id")); + Optional message = messageService.get(idMessage); +// if (req.getParameter("action").equals("delete")) { +// messageService.delete(message.get()); +// resp.sendRedirect("/messages"); +// return; +// } String rememberMe = req.getParameter("rememberMe"); HttpSession session = req.getSession(); if (rememberMe != null && (rememberMe.equals("on") || rememberMe.equals("off"))) { - Long idMessage = Long.parseLong(req.getParameter("id")); - Optional message = messageService.get(idMessage); message.ifPresent(value -> value.setCompleted(true)); messageService.update(message.get()); } diff --git a/src/main/java/com/javarush/goncharov/repository/MessageRepository.java b/src/main/java/com/javarush/goncharov/repository/MessageRepository.java index bcb86b8..eeec414 100644 --- a/src/main/java/com/javarush/goncharov/repository/MessageRepository.java +++ b/src/main/java/com/javarush/goncharov/repository/MessageRepository.java @@ -49,7 +49,7 @@ public void delete(Message message) { @Override public void update(Message message) { if (map.containsKey(message.getId())){ - message.setId(id.incrementAndGet()); +// message.setId(id.incrementAndGet()); message.setName(message.getName()); message.setEmail(message.getEmail()); message.setMessage(message.getMessage()); diff --git a/src/main/webapp/WEB-INF/completed-messages.jsp b/src/main/webapp/WEB-INF/completed-messages.jsp new file mode 100644 index 0000000..6e7e90a --- /dev/null +++ b/src/main/webapp/WEB-INF/completed-messages.jsp @@ -0,0 +1,50 @@ +<%@ page contentType="text/html;charset=UTF-8" language="java" %> +<%@include file="parts/header.jsp" %> + + +
    +
    +
    +

    Обработанные сообщения полученные через форму обратной связи

    +
    +
    +
    + + + +
    + <%--
    --%> +
    +
    + + + +
    +
    + +

    ${message.topic}

    +

    ${message.message}

    +
    + +
    +
    + checked> + +
    +

    ${message.name} - ${message.email}

    +
    +
    +
    +
    + + + + +
    + + +<%@include file="parts/footer.jsp" %> diff --git a/src/main/webapp/WEB-INF/delete-message.jsp b/src/main/webapp/WEB-INF/delete-message.jsp deleted file mode 100644 index 8c59924..0000000 --- a/src/main/webapp/WEB-INF/delete-message.jsp +++ /dev/null @@ -1,24 +0,0 @@ -<%@ page contentType="text/html;charset=UTF-8" language="java" %> -<%@include file="parts/header.jsp" %> - - -
    -
    -
    -
    -
    -

    Удалить сообщение?

    -
    -
    - - -
    -
    -
    -
    -
    -
    - -<%@include file="parts/footer.jsp" %> diff --git a/src/main/webapp/WEB-INF/messages.jsp b/src/main/webapp/WEB-INF/messages.jsp index 815d270..bd19337 100644 --- a/src/main/webapp/WEB-INF/messages.jsp +++ b/src/main/webapp/WEB-INF/messages.jsp @@ -5,7 +5,7 @@
    -

    Сообщения отправленные через форму обратной связи

    +

    Сообщения полученные через форму обратной связи

    @@ -27,8 +27,7 @@

    ${message.message}

    -
    @@ -40,13 +39,24 @@

    ${message.name} - ${message.email}

    - <%--
    --%> - <%-- --%> - <%--
    --%>
    +<%-- --%> +
    + +
    From bad384f3b0aaa08cb6614dfa292bcf7a626eb375 Mon Sep 17 00:00:00 2001 From: GDGo <50753433+GDGo@users.noreply.github.com> Date: Mon, 2 Feb 2026 23:09:20 +0300 Subject: [PATCH 35/50] completed work with aiive messages --- .../controller/CompletedMessages.java | 9 +++- .../goncharov/controller/CreateGame.java | 23 ++++++++++ .../goncharov/controller/Messages.java | 20 ++++++--- .../webapp/WEB-INF/completed-messages.jsp | 11 +++-- src/main/webapp/WEB-INF/create-quest.jsp | 20 ++++----- src/main/webapp/WEB-INF/messages.jsp | 44 +++++++++++-------- src/main/webapp/WEB-INF/parts/header.jsp | 2 +- 7 files changed, 87 insertions(+), 42 deletions(-) create mode 100644 src/main/java/com/javarush/goncharov/controller/CreateGame.java diff --git a/src/main/java/com/javarush/goncharov/controller/CompletedMessages.java b/src/main/java/com/javarush/goncharov/controller/CompletedMessages.java index e74ffd4..85d2bda 100644 --- a/src/main/java/com/javarush/goncharov/controller/CompletedMessages.java +++ b/src/main/java/com/javarush/goncharov/controller/CompletedMessages.java @@ -1,10 +1,13 @@ package com.javarush.goncharov.controller; import com.javarush.goncharov.model.Message; +import com.javarush.goncharov.model.Topic; import com.javarush.goncharov.model.User; import com.javarush.goncharov.repository.MessageRepository; import com.javarush.goncharov.repository.Storage; import com.javarush.goncharov.service.MessageService; +import jakarta.servlet.ServletConfig; +import jakarta.servlet.ServletContext; import jakarta.servlet.ServletException; import jakarta.servlet.annotation.WebServlet; import jakarta.servlet.http.HttpServlet; @@ -33,11 +36,15 @@ protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws Se @Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { + if (req.getParameter("action").equals("GoMessages")) { + resp.sendRedirect("/messages"); + return; + } Long idMessage = Long.parseLong(req.getParameter("id")); Optional message = messageService.get(idMessage); if (req.getParameter("action").equals("delete")) { messageService.delete(message.get()); + resp.sendRedirect("/completed-messages"); } - resp.sendRedirect("/completed-messages"); } } diff --git a/src/main/java/com/javarush/goncharov/controller/CreateGame.java b/src/main/java/com/javarush/goncharov/controller/CreateGame.java new file mode 100644 index 0000000..b99f6ae --- /dev/null +++ b/src/main/java/com/javarush/goncharov/controller/CreateGame.java @@ -0,0 +1,23 @@ +package com.javarush.goncharov.controller; + +import jakarta.servlet.ServletException; +import jakarta.servlet.annotation.WebServlet; +import jakarta.servlet.http.HttpServlet; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; + +import java.io.IOException; + +@WebServlet("/create-quest") +public class CreateGame extends HttpServlet { + @Override + protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { + req.getRequestDispatcher("/WEB-INF/create-quest.jsp").forward(req, resp); + } + + @Override + protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { + String name = req.getParameter("name"); + String text = req.getParameter("text"); + } +} diff --git a/src/main/java/com/javarush/goncharov/controller/Messages.java b/src/main/java/com/javarush/goncharov/controller/Messages.java index 3bb4b1e..e622ef6 100644 --- a/src/main/java/com/javarush/goncharov/controller/Messages.java +++ b/src/main/java/com/javarush/goncharov/controller/Messages.java @@ -1,10 +1,14 @@ package com.javarush.goncharov.controller; import com.javarush.goncharov.model.Message; +import com.javarush.goncharov.model.Role; +import com.javarush.goncharov.model.Topic; import com.javarush.goncharov.model.User; import com.javarush.goncharov.repository.MessageRepository; import com.javarush.goncharov.repository.Storage; import com.javarush.goncharov.service.MessageService; +import jakarta.servlet.ServletConfig; +import jakarta.servlet.ServletContext; import jakarta.servlet.ServletException; import jakarta.servlet.annotation.WebServlet; import jakarta.servlet.http.HttpServlet; @@ -21,6 +25,12 @@ public class Messages extends HttpServlet { private final Storage messageStorage = Storage.getInstance(); private final MessageService messageService = new MessageService(new MessageRepository(messageStorage)); + @Override + public void init(ServletConfig config) throws ServletException { + ServletContext servletContext = config.getServletContext(); + servletContext.setAttribute("topics", Topic.values()); + } + @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { Collection messages = messageService.getAll().values(); @@ -33,23 +43,19 @@ protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws Se @Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { - if (req.getParameter("action").equals("completedValues")) { + if (req.getParameter("action").equals("GoArchive")) { resp.sendRedirect("/completed-messages"); return; } Long idMessage = Long.parseLong(req.getParameter("id")); Optional message = messageService.get(idMessage); -// if (req.getParameter("action").equals("delete")) { -// messageService.delete(message.get()); -// resp.sendRedirect("/messages"); -// return; -// } String rememberMe = req.getParameter("rememberMe"); HttpSession session = req.getSession(); if (rememberMe != null && (rememberMe.equals("on") || rememberMe.equals("off"))) { message.ifPresent(value -> value.setCompleted(true)); - messageService.update(message.get()); } + message.ifPresent(value -> value.setTopic(Topic.valueOf(req.getParameter("topic")))); + messageService.update(message.get()); resp.sendRedirect("/messages"); } } diff --git a/src/main/webapp/WEB-INF/completed-messages.jsp b/src/main/webapp/WEB-INF/completed-messages.jsp index 6e7e90a..0732078 100644 --- a/src/main/webapp/WEB-INF/completed-messages.jsp +++ b/src/main/webapp/WEB-INF/completed-messages.jsp @@ -25,16 +25,16 @@

    ${message.topic}

    ${message.message}

    -
    - -
    checked>
    +
    + +

    ${message.name} - ${message.email}

    @@ -42,6 +42,9 @@ +
    + +
    diff --git a/src/main/webapp/WEB-INF/create-quest.jsp b/src/main/webapp/WEB-INF/create-quest.jsp index afdb81d..2e8853b 100644 --- a/src/main/webapp/WEB-INF/create-quest.jsp +++ b/src/main/webapp/WEB-INF/create-quest.jsp @@ -30,15 +30,15 @@ - +<%----%> diff --git a/src/main/webapp/WEB-INF/messages.jsp b/src/main/webapp/WEB-INF/messages.jsp index bd19337..9cb93e6 100644 --- a/src/main/webapp/WEB-INF/messages.jsp +++ b/src/main/webapp/WEB-INF/messages.jsp @@ -23,19 +23,25 @@
    -

    ${message.topic}

    -

    ${message.message}

    -
    - -<%-- +
    +
    +

    ${message.message}

    checked> + checked>
    +
    + + +

    ${message.name} - ${message.email}

    @@ -43,19 +49,19 @@ -<%-- --%> + <%-- --%>
    - +
    diff --git a/src/main/webapp/WEB-INF/parts/header.jsp b/src/main/webapp/WEB-INF/parts/header.jsp index 0eaee7c..238d15c 100644 --- a/src/main/webapp/WEB-INF/parts/header.jsp +++ b/src/main/webapp/WEB-INF/parts/header.jsp @@ -41,7 +41,7 @@ - + From fcf4489fa9ed3080cf5a02f1a8e5b162f8751eb1 Mon Sep 17 00:00:00 2001 From: GDGo <50753433+GDGo@users.noreply.github.com> Date: Wed, 4 Feb 2026 01:34:33 +0300 Subject: [PATCH 36/50] start add logic create quest --- .../goncharov/controller/CreateGame.java | 18 ++++++ .../com/javarush/goncharov/model/Answer.java | 14 +++++ .../com/javarush/goncharov/model/Quest.java | 17 ++++++ .../javarush/goncharov/model/Question.java | 17 ++++++ .../com/javarush/goncharov/model/User.java | 4 ++ .../goncharov/repository/QuestRepository.java | 51 ++++++++++++++++ .../repository/QuestionRepository.java | 4 ++ .../goncharov/repository/Storage.java | 8 +-- .../goncharov/service/QuestService.java | 61 +++++++++++++++++++ .../goncharov/service/QuestionService.java | 4 ++ src/main/webapp/WEB-INF/create-quest.jsp | 22 +++---- 11 files changed, 205 insertions(+), 15 deletions(-) create mode 100644 src/main/java/com/javarush/goncharov/model/Answer.java create mode 100644 src/main/java/com/javarush/goncharov/model/Quest.java create mode 100644 src/main/java/com/javarush/goncharov/model/Question.java create mode 100644 src/main/java/com/javarush/goncharov/repository/QuestRepository.java create mode 100644 src/main/java/com/javarush/goncharov/repository/QuestionRepository.java create mode 100644 src/main/java/com/javarush/goncharov/service/QuestService.java create mode 100644 src/main/java/com/javarush/goncharov/service/QuestionService.java diff --git a/src/main/java/com/javarush/goncharov/controller/CreateGame.java b/src/main/java/com/javarush/goncharov/controller/CreateGame.java index b99f6ae..8df2dba 100644 --- a/src/main/java/com/javarush/goncharov/controller/CreateGame.java +++ b/src/main/java/com/javarush/goncharov/controller/CreateGame.java @@ -1,15 +1,28 @@ package com.javarush.goncharov.controller; +import com.javarush.goncharov.model.Quest; +import com.javarush.goncharov.model.User; +import com.javarush.goncharov.repository.QuestRepository; +import com.javarush.goncharov.repository.Storage; +import com.javarush.goncharov.repository.UserRepository; +import com.javarush.goncharov.service.QuestService; +import com.javarush.goncharov.service.UserService; import jakarta.servlet.ServletException; import jakarta.servlet.annotation.WebServlet; import jakarta.servlet.http.HttpServlet; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; +import jakarta.servlet.http.HttpSession; import java.io.IOException; +import java.util.Optional; @WebServlet("/create-quest") public class CreateGame extends HttpServlet { + private final Storage storage = Storage.getInstance(); + private final UserService userService = new UserService(new UserRepository(storage)); + private final QuestService questService = new QuestService(new QuestRepository(storage)); + @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { req.getRequestDispatcher("/WEB-INF/create-quest.jsp").forward(req, resp); @@ -17,7 +30,12 @@ protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws Se @Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { + HttpSession session = req.getSession(); + User userSession = (User) session.getAttribute("user"); + Optional userFind = userService.get(userSession.getId()); String name = req.getParameter("name"); String text = req.getParameter("text"); + questService.create(name, text, userFind.get().getId(), userFind.get().getLogin()); + System.out.println(questService.getAll()); } } diff --git a/src/main/java/com/javarush/goncharov/model/Answer.java b/src/main/java/com/javarush/goncharov/model/Answer.java new file mode 100644 index 0000000..1bc2108 --- /dev/null +++ b/src/main/java/com/javarush/goncharov/model/Answer.java @@ -0,0 +1,14 @@ +package com.javarush.goncharov.model; + +import lombok.*; + +@AllArgsConstructor +@NoArgsConstructor +@Data +@Builder +public class Answer { + private Long id; + private Long questionId; + private String text; + private Long nextQuestionId; +} diff --git a/src/main/java/com/javarush/goncharov/model/Quest.java b/src/main/java/com/javarush/goncharov/model/Quest.java new file mode 100644 index 0000000..6643469 --- /dev/null +++ b/src/main/java/com/javarush/goncharov/model/Quest.java @@ -0,0 +1,17 @@ +package com.javarush.goncharov.model; + + +import lombok.*; + +@AllArgsConstructor +@NoArgsConstructor +@Data +@Builder +public class Quest{ + private Long id; + private String name; + private String authorName; + private String text; + private Long developerId; + private Long startQuestionId; +} diff --git a/src/main/java/com/javarush/goncharov/model/Question.java b/src/main/java/com/javarush/goncharov/model/Question.java new file mode 100644 index 0000000..31b7c26 --- /dev/null +++ b/src/main/java/com/javarush/goncharov/model/Question.java @@ -0,0 +1,17 @@ +package com.javarush.goncharov.model; + +import java.util.ArrayList; +import java.util.Collection; + +import lombok.*; + +@AllArgsConstructor +@NoArgsConstructor +@Data +@Builder +public class Question { + private Long id; + private Long questId; + private String text; + private final Collection answers = new ArrayList<>(); +} diff --git a/src/main/java/com/javarush/goncharov/model/User.java b/src/main/java/com/javarush/goncharov/model/User.java index 4b55786..636a6c6 100644 --- a/src/main/java/com/javarush/goncharov/model/User.java +++ b/src/main/java/com/javarush/goncharov/model/User.java @@ -5,6 +5,9 @@ import lombok.Data; import lombok.NoArgsConstructor; +import java.util.ArrayList; +import java.util.Collection; + @NoArgsConstructor @AllArgsConstructor @Data @@ -15,4 +18,5 @@ public class User{ private String password; private Role role; private String email; + private final Collection quests = new ArrayList<>(); } diff --git a/src/main/java/com/javarush/goncharov/repository/QuestRepository.java b/src/main/java/com/javarush/goncharov/repository/QuestRepository.java new file mode 100644 index 0000000..8796469 --- /dev/null +++ b/src/main/java/com/javarush/goncharov/repository/QuestRepository.java @@ -0,0 +1,51 @@ +package com.javarush.goncharov.repository; + +import com.javarush.goncharov.model.Quest; + +import java.util.Map; +import java.util.Optional; +import java.util.concurrent.atomic.AtomicLong; + +public class QuestRepository implements Repository{ + private final Map map; + public static final AtomicLong id = new AtomicLong(); + + public QuestRepository(Storage messageStorage) { + this.map = messageStorage.getQuests(); + } + + @Override + public Optional get(long id) { + return Optional.ofNullable(map.get(id)); + } + + @Override + public Optional findBy(String name, String authorName) { + return map.values() + .stream() + .filter(u -> u.getName().equals(name)) + .filter(u -> u.getAuthorName().equals(authorName)) + .findAny(); + } + + @Override + public Map getAll() { + return map; + } + + @Override + public void create(Quest message) { + message.setId(id.incrementAndGet()); + update(message); + } + + @Override + public void delete(Quest message) { + map.remove(message.getId()); + } + + @Override + public void update(Quest message) { + map.put(message.getId(), message); + } +} diff --git a/src/main/java/com/javarush/goncharov/repository/QuestionRepository.java b/src/main/java/com/javarush/goncharov/repository/QuestionRepository.java new file mode 100644 index 0000000..cc68fe3 --- /dev/null +++ b/src/main/java/com/javarush/goncharov/repository/QuestionRepository.java @@ -0,0 +1,4 @@ +package com.javarush.goncharov.repository; + +public class QuestionRepository { +} diff --git a/src/main/java/com/javarush/goncharov/repository/Storage.java b/src/main/java/com/javarush/goncharov/repository/Storage.java index 42da4d3..1a38213 100644 --- a/src/main/java/com/javarush/goncharov/repository/Storage.java +++ b/src/main/java/com/javarush/goncharov/repository/Storage.java @@ -1,9 +1,6 @@ package com.javarush.goncharov.repository; -import com.javarush.goncharov.model.Message; -import com.javarush.goncharov.model.Role; -import com.javarush.goncharov.model.Topic; -import com.javarush.goncharov.model.User; +import com.javarush.goncharov.model.*; import lombok.Getter; import java.util.Map; @@ -14,6 +11,9 @@ public class Storage { private static Storage instance; private final Map users = new ConcurrentHashMap<>(); private final Map messages = new ConcurrentHashMap<>(); + private final Map quests = new ConcurrentHashMap<>(); + private final Map questions = new ConcurrentHashMap<>(); + private final Map answers = new ConcurrentHashMap<>(); private Storage() { users.put(1L, new User(1L, "Admin", "123", Role.ADMIN, "admin@test.ru")); diff --git a/src/main/java/com/javarush/goncharov/service/QuestService.java b/src/main/java/com/javarush/goncharov/service/QuestService.java new file mode 100644 index 0000000..8351f60 --- /dev/null +++ b/src/main/java/com/javarush/goncharov/service/QuestService.java @@ -0,0 +1,61 @@ +package com.javarush.goncharov.service; + +import com.javarush.goncharov.model.Quest; +import com.javarush.goncharov.model.User; +import com.javarush.goncharov.repository.QuestRepository; +import com.javarush.goncharov.repository.Repository; +import com.javarush.goncharov.repository.Storage; +import com.javarush.goncharov.repository.UserRepository; + +import java.util.Collection; +import java.util.Map; +import java.util.Optional; + + +public class QuestService { + private final Storage storage = Storage.getInstance(); + private final UserService userService = new UserService(new UserRepository(storage)); +// private final QuestService questService = new QuestService(new QuestRepository(storage)); + + private final Repository questRepository; + + public QuestService(Repository questRepository) { + this.questRepository = questRepository; + } + + public Optional get(Long id){ + return questRepository.get(id); + } + + public Optional find(String name, String authorName){ + return questRepository.findBy(name, authorName); + } + + public void create(String name, String text, Long idAuthor, String nameAuthor){ + Quest quest = Quest.builder() + .name(name) + .authorName(nameAuthor) + .text(text) + .developerId(idAuthor) + .startQuestionId(0L) + .build(); + questRepository.create(quest); + Optional userFind = userService.get(idAuthor); + Collection quests = userFind.get().getQuests(); + quests.add(quest); + this.update(quest); + System.out.println(userFind.get().getQuests()); + } + + public void delete(Quest quest){ + questRepository.delete(quest); + } + + public void update(Quest quest){ + questRepository.update(quest); + } + + public Map getAll(){ + return questRepository.getAll(); + } +} diff --git a/src/main/java/com/javarush/goncharov/service/QuestionService.java b/src/main/java/com/javarush/goncharov/service/QuestionService.java new file mode 100644 index 0000000..03abcd4 --- /dev/null +++ b/src/main/java/com/javarush/goncharov/service/QuestionService.java @@ -0,0 +1,4 @@ +package com.javarush.goncharov.service; + +public class QuestionService { +} diff --git a/src/main/webapp/WEB-INF/create-quest.jsp b/src/main/webapp/WEB-INF/create-quest.jsp index 2e8853b..5fff8b9 100644 --- a/src/main/webapp/WEB-INF/create-quest.jsp +++ b/src/main/webapp/WEB-INF/create-quest.jsp @@ -13,7 +13,7 @@
    + placeholder="<%@include file="/WEB-INF/parts/quest-demo.jsp" %>">
    @@ -30,15 +30,15 @@
    -<%----%> + From 0c4303cbe83399d2ebba087cb666c2b120aa3fe4 Mon Sep 17 00:00:00 2001 From: GDGo <50753433+GDGo@users.noreply.github.com> Date: Wed, 4 Feb 2026 22:47:47 +0300 Subject: [PATCH 37/50] add logic create quest --- .../com/javarush/goncharov/repository/QuestRepository.java | 4 ++-- .../java/com/javarush/goncharov/service/QuestService.java | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/main/java/com/javarush/goncharov/repository/QuestRepository.java b/src/main/java/com/javarush/goncharov/repository/QuestRepository.java index 8796469..182b25f 100644 --- a/src/main/java/com/javarush/goncharov/repository/QuestRepository.java +++ b/src/main/java/com/javarush/goncharov/repository/QuestRepository.java @@ -10,8 +10,8 @@ public class QuestRepository implements Repository{ private final Map map; public static final AtomicLong id = new AtomicLong(); - public QuestRepository(Storage messageStorage) { - this.map = messageStorage.getQuests(); + public QuestRepository(Storage questStorage) { + this.map = questStorage.getQuests(); } @Override diff --git a/src/main/java/com/javarush/goncharov/service/QuestService.java b/src/main/java/com/javarush/goncharov/service/QuestService.java index 8351f60..0dffc33 100644 --- a/src/main/java/com/javarush/goncharov/service/QuestService.java +++ b/src/main/java/com/javarush/goncharov/service/QuestService.java @@ -43,7 +43,7 @@ public void create(String name, String text, Long idAuthor, String nameAuthor){ Optional userFind = userService.get(idAuthor); Collection quests = userFind.get().getQuests(); quests.add(quest); - this.update(quest); + questRepository.update(quest); System.out.println(userFind.get().getQuests()); } From d4aad79871ef407da3c06c2bc877811fa9184ed2 Mon Sep 17 00:00:00 2001 From: GDGo <50753433+GDGo@users.noreply.github.com> Date: Thu, 5 Feb 2026 00:22:50 +0300 Subject: [PATCH 38/50] add logic create quest, add question repo, add question service --- .../com/javarush/goncharov/model/Quest.java | 4 ++ .../javarush/goncharov/model/Question.java | 1 + .../repository/QuestionRepository.java | 49 ++++++++++++++++++- .../goncharov/service/QuestService.java | 32 ++++++++++-- .../goncharov/service/QuestionService.java | 36 ++++++++++++++ 5 files changed, 117 insertions(+), 5 deletions(-) diff --git a/src/main/java/com/javarush/goncharov/model/Quest.java b/src/main/java/com/javarush/goncharov/model/Quest.java index 6643469..79eb200 100644 --- a/src/main/java/com/javarush/goncharov/model/Quest.java +++ b/src/main/java/com/javarush/goncharov/model/Quest.java @@ -3,6 +3,9 @@ import lombok.*; +import java.util.ArrayList; +import java.util.Collection; + @AllArgsConstructor @NoArgsConstructor @Data @@ -14,4 +17,5 @@ public class Quest{ private String text; private Long developerId; private Long startQuestionId; + private final Collection questions = new ArrayList<>(); } diff --git a/src/main/java/com/javarush/goncharov/model/Question.java b/src/main/java/com/javarush/goncharov/model/Question.java index 31b7c26..58fea97 100644 --- a/src/main/java/com/javarush/goncharov/model/Question.java +++ b/src/main/java/com/javarush/goncharov/model/Question.java @@ -12,6 +12,7 @@ public class Question { private Long id; private Long questId; + private String questName; private String text; private final Collection answers = new ArrayList<>(); } diff --git a/src/main/java/com/javarush/goncharov/repository/QuestionRepository.java b/src/main/java/com/javarush/goncharov/repository/QuestionRepository.java index cc68fe3..9ef998a 100644 --- a/src/main/java/com/javarush/goncharov/repository/QuestionRepository.java +++ b/src/main/java/com/javarush/goncharov/repository/QuestionRepository.java @@ -1,4 +1,51 @@ package com.javarush.goncharov.repository; -public class QuestionRepository { +import com.javarush.goncharov.model.Question; + +import java.util.Map; +import java.util.Optional; +import java.util.concurrent.atomic.AtomicLong; + +public class QuestionRepository implements Repository{ + private final Map map; + public static final AtomicLong id = new AtomicLong(); + + public QuestionRepository(Storage questionStorage) { + this.map = questionStorage.getQuestions(); + } + + @Override + public Optional get(long id) { + return Optional.ofNullable(map.get(id)); + } + + @Override + public Optional findBy(String questName, String text) { + return map.values() + .stream() + .filter(u -> u.getQuestName().equals(questName)) + .filter(u -> u.getText().equals(text)) + .findAny(); + } + + @Override + public Map getAll() { + return map; + } + + @Override + public void create(Question question) { + question.setId(id.incrementAndGet()); + update(question); + } + + @Override + public void delete(Question question) { + map.remove(question.getId()); + } + + @Override + public void update(Question question) { + map.put(question.getId(), question); + } } diff --git a/src/main/java/com/javarush/goncharov/service/QuestService.java b/src/main/java/com/javarush/goncharov/service/QuestService.java index 0dffc33..7b06a80 100644 --- a/src/main/java/com/javarush/goncharov/service/QuestService.java +++ b/src/main/java/com/javarush/goncharov/service/QuestService.java @@ -1,8 +1,9 @@ package com.javarush.goncharov.service; import com.javarush.goncharov.model.Quest; +import com.javarush.goncharov.model.Question; import com.javarush.goncharov.model.User; -import com.javarush.goncharov.repository.QuestRepository; +import com.javarush.goncharov.repository.QuestionRepository; import com.javarush.goncharov.repository.Repository; import com.javarush.goncharov.repository.Storage; import com.javarush.goncharov.repository.UserRepository; @@ -10,13 +11,14 @@ import java.util.Collection; import java.util.Map; import java.util.Optional; +import java.util.regex.Pattern; +import java.util.regex.Matcher; public class QuestService { private final Storage storage = Storage.getInstance(); private final UserService userService = new UserService(new UserRepository(storage)); -// private final QuestService questService = new QuestService(new QuestRepository(storage)); - + private final QuestionService questionService = new QuestionService(new QuestionRepository(storage)); private final Repository questRepository; public QuestService(Repository questRepository) { @@ -32,6 +34,10 @@ public Optional find(String name, String authorName){ } public void create(String name, String text, Long idAuthor, String nameAuthor){ + String patternQ = "(\\d+):\\s+(.*)"; + String patternA = "\\d+<.*"; + String patternL = "\\d+-.*"; + String patternW = "\\d+\\+.*"; Quest quest = Quest.builder() .name(name) .authorName(nameAuthor) @@ -44,7 +50,25 @@ public void create(String name, String text, Long idAuthor, String nameAuthor){ Collection quests = userFind.get().getQuests(); quests.add(quest); questRepository.update(quest); - System.out.println(userFind.get().getQuests()); + Pattern patternQuestion = Pattern.compile(patternQ); + Matcher matcherQuestion = patternQuestion.matcher(text); + Collection questionsQuest = quest.getQuestions(); + while (matcherQuestion.find()) { + Integer idQuestionOnQuest = Integer.parseInt(matcherQuestion.group(1)); + String textQuestion = matcherQuestion.group(2); + Question question = Question.builder() + .questId(quest.getId()) + .questName(quest.getName()) + .text(textQuestion) + .build(); + questionService.post(question); + if (idQuestionOnQuest.equals(1)){ + quest.setStartQuestionId(question.getId()); + } + questionsQuest.add(question); + } + questRepository.update(quest); + System.out.println(questionService.getAll()); } public void delete(Quest quest){ diff --git a/src/main/java/com/javarush/goncharov/service/QuestionService.java b/src/main/java/com/javarush/goncharov/service/QuestionService.java index 03abcd4..226e827 100644 --- a/src/main/java/com/javarush/goncharov/service/QuestionService.java +++ b/src/main/java/com/javarush/goncharov/service/QuestionService.java @@ -1,4 +1,40 @@ package com.javarush.goncharov.service; +import com.javarush.goncharov.model.Question; +import com.javarush.goncharov.repository.Repository; + +import java.util.Map; +import java.util.Optional; + public class QuestionService { + private final Repository questionRepository; + + public QuestionService(Repository questionRepository) { + this.questionRepository = questionRepository; + } + + public Optional get(Long id){ + return questionRepository.get(id); + } + + public Optional find(String nameQuest, String text){ + return questionRepository.findBy(nameQuest, text); + } + + public void post(Question question){ + question.setId(0L); + questionRepository.create(question); + } + + public void delete(Question question){ + questionRepository.delete(question); + } + + public void update(Question question){ + questionRepository.update(question); + } + + public Map getAll(){ + return questionRepository.getAll(); + } } From 1e221d77d547305a18521cef940edc6c01188e8c Mon Sep 17 00:00:00 2001 From: GDGo <50753433+GDGo@users.noreply.github.com> Date: Thu, 5 Feb 2026 11:45:50 +0300 Subject: [PATCH 39/50] add logic create quest, add question repo, add question service #2 --- .../com/javarush/goncharov/model/Answer.java | 1 + .../repository/AnswerRepository.java | 52 ++++++++++++++ .../goncharov/service/QuestService.java | 70 ++++++++++++------- 3 files changed, 99 insertions(+), 24 deletions(-) create mode 100644 src/main/java/com/javarush/goncharov/repository/AnswerRepository.java diff --git a/src/main/java/com/javarush/goncharov/model/Answer.java b/src/main/java/com/javarush/goncharov/model/Answer.java index 1bc2108..03ca67c 100644 --- a/src/main/java/com/javarush/goncharov/model/Answer.java +++ b/src/main/java/com/javarush/goncharov/model/Answer.java @@ -10,5 +10,6 @@ public class Answer { private Long id; private Long questionId; private String text; + private String questName; private Long nextQuestionId; } diff --git a/src/main/java/com/javarush/goncharov/repository/AnswerRepository.java b/src/main/java/com/javarush/goncharov/repository/AnswerRepository.java new file mode 100644 index 0000000..b42f7e6 --- /dev/null +++ b/src/main/java/com/javarush/goncharov/repository/AnswerRepository.java @@ -0,0 +1,52 @@ +package com.javarush.goncharov.repository; + +import com.javarush.goncharov.model.Answer; + +import java.util.Map; +import java.util.Optional; +import java.util.concurrent.atomic.AtomicLong; + +public class AnswerRepository implements Repository{ + private final Map map; + public static final AtomicLong id = new AtomicLong(); + + public AnswerRepository(Storage answerStorage) { + this.map = answerStorage.getAnswers(); + } + + @Override + public Optional get(long id) { + return Optional.ofNullable(map.get(id)); + } + + @Override + public Optional findBy(String questName, String text) { + return map.values() + .stream() + .filter(u -> u.getQuestName().equals(questName)) + .filter(u -> u.getText().equals(text)) + .findAny(); + } + + @Override + public Map getAll() { + return map; + } + + @Override + public void create(Answer answer) { + answer.setId(id.incrementAndGet()); + update(answer); + } + + @Override + public void delete(Answer answer) { + map.remove(answer.getId()); + } + + @Override + public void update(Answer answer) { + map.put(answer.getId(), answer); + } +} + diff --git a/src/main/java/com/javarush/goncharov/service/QuestService.java b/src/main/java/com/javarush/goncharov/service/QuestService.java index 7b06a80..c0d0d4f 100644 --- a/src/main/java/com/javarush/goncharov/service/QuestService.java +++ b/src/main/java/com/javarush/goncharov/service/QuestService.java @@ -1,12 +1,10 @@ package com.javarush.goncharov.service; +import com.javarush.goncharov.model.Answer; import com.javarush.goncharov.model.Quest; import com.javarush.goncharov.model.Question; import com.javarush.goncharov.model.User; -import com.javarush.goncharov.repository.QuestionRepository; -import com.javarush.goncharov.repository.Repository; -import com.javarush.goncharov.repository.Storage; -import com.javarush.goncharov.repository.UserRepository; +import com.javarush.goncharov.repository.*; import java.util.Collection; import java.util.Map; @@ -19,6 +17,7 @@ public class QuestService { private final Storage storage = Storage.getInstance(); private final UserService userService = new UserService(new UserRepository(storage)); private final QuestionService questionService = new QuestionService(new QuestionRepository(storage)); + private final AnswerRepository answerRepository = new AnswerRepository(storage); private final Repository questRepository; public QuestService(Repository questRepository) { @@ -34,10 +33,6 @@ public Optional find(String name, String authorName){ } public void create(String name, String text, Long idAuthor, String nameAuthor){ - String patternQ = "(\\d+):\\s+(.*)"; - String patternA = "\\d+<.*"; - String patternL = "\\d+-.*"; - String patternW = "\\d+\\+.*"; Quest quest = Quest.builder() .name(name) .authorName(nameAuthor) @@ -46,29 +41,56 @@ public void create(String name, String text, Long idAuthor, String nameAuthor){ .startQuestionId(0L) .build(); questRepository.create(quest); + getQuestions(quest); Optional userFind = userService.get(idAuthor); Collection quests = userFind.get().getQuests(); quests.add(quest); + getQuestions(quest); questRepository.update(quest); - Pattern patternQuestion = Pattern.compile(patternQ); - Matcher matcherQuestion = patternQuestion.matcher(text); - Collection questionsQuest = quest.getQuestions(); - while (matcherQuestion.find()) { - Integer idQuestionOnQuest = Integer.parseInt(matcherQuestion.group(1)); - String textQuestion = matcherQuestion.group(2); - Question question = Question.builder() - .questId(quest.getId()) - .questName(quest.getName()) - .text(textQuestion) - .build(); - questionService.post(question); - if (idQuestionOnQuest.equals(1)){ - quest.setStartQuestionId(question.getId()); + System.out.println(questionService.getAll()); + } + + private void getQuestions(Quest quest) { + Matcher matcher = getMatcher(quest); + Question question = new Question(); + while (matcher.find()) { + Long idQuestion = Long.parseLong(matcher.group(1)); + String symbolQuestion = matcher.group(2); + String text = matcher.group(3); + if (symbolQuestion.equals(":") || + symbolQuestion.equals("+") || + symbolQuestion.equals("-")){ + question = Question.builder() + .questId(quest.getId()) + .questName(quest.getName()) + .text(text) + .build(); + questionService.post(question); + question.setId(idQuestion); + questionService.update(question); + quest.getQuestions().add(question); + if (question.getId().equals(1L)){ + quest.setStartQuestionId(question.getId()); + } + } + if (symbolQuestion.equals("<")){ + Answer answer = Answer.builder() + .questionId(question.getId()) + .nextQuestionId(idQuestion) + .text(text) + .questName(quest.getName()) + .build(); + answerRepository.create(answer); + question.getAnswers().add(answer); } - questionsQuest.add(question); } questRepository.update(quest); - System.out.println(questionService.getAll()); + } + + private static Matcher getMatcher(Quest quest) { + String patternQ = "(\\d+)\\s*([:<\\-+])\\s*(.*)"; + Pattern pattern = Pattern.compile(patternQ); + return pattern.matcher(quest.getText()); } public void delete(Quest quest){ From 7d21e6c41e3328ac2e8f43131ac52378b3459798 Mon Sep 17 00:00:00 2001 From: GDGo <50753433+GDGo@users.noreply.github.com> Date: Thu, 5 Feb 2026 23:38:31 +0300 Subject: [PATCH 40/50] add logic create quest, add question repo, add question service #3 --- .../goncharov/controller/CreateGame.java | 3 +- .../goncharov/controller/ListQuests.java | 33 ++++++++++++ .../goncharov/controller/PlayGame.java | 8 +++ .../com/javarush/goncharov/model/Game.java | 20 ++++++++ .../javarush/goncharov/model/GameState.java | 5 ++ .../goncharov/repository/GameRepository.java | 51 +++++++++++++++++++ .../goncharov/repository/Storage.java | 1 + .../goncharov/service/GameService.java | 45 ++++++++++++++++ src/main/webapp/WEB-INF/parts/header.jsp | 2 +- 9 files changed, 166 insertions(+), 2 deletions(-) create mode 100644 src/main/java/com/javarush/goncharov/controller/ListQuests.java create mode 100644 src/main/java/com/javarush/goncharov/controller/PlayGame.java create mode 100644 src/main/java/com/javarush/goncharov/model/Game.java create mode 100644 src/main/java/com/javarush/goncharov/model/GameState.java create mode 100644 src/main/java/com/javarush/goncharov/repository/GameRepository.java create mode 100644 src/main/java/com/javarush/goncharov/service/GameService.java diff --git a/src/main/java/com/javarush/goncharov/controller/CreateGame.java b/src/main/java/com/javarush/goncharov/controller/CreateGame.java index 8df2dba..5e8e577 100644 --- a/src/main/java/com/javarush/goncharov/controller/CreateGame.java +++ b/src/main/java/com/javarush/goncharov/controller/CreateGame.java @@ -13,6 +13,7 @@ import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; import jakarta.servlet.http.HttpSession; +import lombok.AllArgsConstructor; import java.io.IOException; import java.util.Optional; @@ -36,6 +37,6 @@ protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws S String name = req.getParameter("name"); String text = req.getParameter("text"); questService.create(name, text, userFind.get().getId(), userFind.get().getLogin()); - System.out.println(questService.getAll()); + resp.sendRedirect("/list-quests"); } } diff --git a/src/main/java/com/javarush/goncharov/controller/ListQuests.java b/src/main/java/com/javarush/goncharov/controller/ListQuests.java new file mode 100644 index 0000000..2d81995 --- /dev/null +++ b/src/main/java/com/javarush/goncharov/controller/ListQuests.java @@ -0,0 +1,33 @@ +package com.javarush.goncharov.controller; + +import com.javarush.goncharov.model.Quest; +import com.javarush.goncharov.model.User; +import com.javarush.goncharov.repository.QuestRepository; +import com.javarush.goncharov.repository.Storage; +import com.javarush.goncharov.service.QuestService; +import jakarta.servlet.ServletException; +import jakarta.servlet.annotation.WebServlet; +import jakarta.servlet.http.HttpServlet; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import jakarta.servlet.http.HttpSession; + +import java.io.IOException; +import java.util.Collection; + +@WebServlet("/list-quests") +public class ListQuests extends HttpServlet { + + private final Storage storage = Storage.getInstance(); + private final QuestService questService = new QuestService(new QuestRepository(storage)); + + @Override + protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { + Collection quests = questService.getAll().values(); + HttpSession session = req.getSession(); + User user = (User) session.getAttribute("userSession"); + req.setAttribute("quests", quests); + req.setAttribute("user", user); + req.getRequestDispatcher("/WEB-INF/home.jsp").forward(req, resp); + } +} diff --git a/src/main/java/com/javarush/goncharov/controller/PlayGame.java b/src/main/java/com/javarush/goncharov/controller/PlayGame.java new file mode 100644 index 0000000..5a2ee2e --- /dev/null +++ b/src/main/java/com/javarush/goncharov/controller/PlayGame.java @@ -0,0 +1,8 @@ +package com.javarush.goncharov.controller; + +import jakarta.servlet.annotation.WebServlet; +import jakarta.servlet.http.HttpServlet; + +@WebServlet("/play-game") +public class PlayGame extends HttpServlet { +} diff --git a/src/main/java/com/javarush/goncharov/model/Game.java b/src/main/java/com/javarush/goncharov/model/Game.java new file mode 100644 index 0000000..d3f9779 --- /dev/null +++ b/src/main/java/com/javarush/goncharov/model/Game.java @@ -0,0 +1,20 @@ +package com.javarush.goncharov.model; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +@NoArgsConstructor +@AllArgsConstructor +@Builder +@Data +public class Game { + private Long id; + private Long questId; + private String questName; + private Long userId; + private String userName; + private Long currentQuestionId; + private GameState gameState; +} diff --git a/src/main/java/com/javarush/goncharov/model/GameState.java b/src/main/java/com/javarush/goncharov/model/GameState.java new file mode 100644 index 0000000..92c12e8 --- /dev/null +++ b/src/main/java/com/javarush/goncharov/model/GameState.java @@ -0,0 +1,5 @@ +package com.javarush.goncharov.model; + +public enum GameState { + WIN, LOSE, PLAY +} diff --git a/src/main/java/com/javarush/goncharov/repository/GameRepository.java b/src/main/java/com/javarush/goncharov/repository/GameRepository.java new file mode 100644 index 0000000..1110dea --- /dev/null +++ b/src/main/java/com/javarush/goncharov/repository/GameRepository.java @@ -0,0 +1,51 @@ +package com.javarush.goncharov.repository; + +import com.javarush.goncharov.model.Game; + +import java.util.Map; +import java.util.Optional; +import java.util.concurrent.atomic.AtomicLong; + +public class GameRepository implements Repository{ + private final Map map; + public static final AtomicLong id = new AtomicLong(); + + public GameRepository(Storage gameStorage) { + this.map = gameStorage.getGames(); + } + + @Override + public Optional get(long id) { + return Optional.ofNullable(map.get(id)); + } + + @Override + public Optional findBy(String questName, String userName) { + return map.values() + .stream() + .filter(u -> u.getQuestName().equals(questName)) + .filter(u -> u.getUserName().equals(userName)) + .findAny(); + } + + @Override + public Map getAll() { + return map; + } + + @Override + public void create(Game game) { + game.setId(id.incrementAndGet()); + update(game); + } + + @Override + public void delete(Game game) { + map.remove(game.getId()); + } + + @Override + public void update(Game game) { + map.put(game.getId(), game); + } +} diff --git a/src/main/java/com/javarush/goncharov/repository/Storage.java b/src/main/java/com/javarush/goncharov/repository/Storage.java index 1a38213..a0e1fce 100644 --- a/src/main/java/com/javarush/goncharov/repository/Storage.java +++ b/src/main/java/com/javarush/goncharov/repository/Storage.java @@ -14,6 +14,7 @@ public class Storage { private final Map quests = new ConcurrentHashMap<>(); private final Map questions = new ConcurrentHashMap<>(); private final Map answers = new ConcurrentHashMap<>(); + private final Map games = new ConcurrentHashMap<>(); private Storage() { users.put(1L, new User(1L, "Admin", "123", Role.ADMIN, "admin@test.ru")); diff --git a/src/main/java/com/javarush/goncharov/service/GameService.java b/src/main/java/com/javarush/goncharov/service/GameService.java new file mode 100644 index 0000000..d421c0a --- /dev/null +++ b/src/main/java/com/javarush/goncharov/service/GameService.java @@ -0,0 +1,45 @@ +package com.javarush.goncharov.service; + +import com.javarush.goncharov.model.Game; +import com.javarush.goncharov.model.Quest; +import com.javarush.goncharov.repository.*; + +import java.util.Map; +import java.util.Optional; + +public class GameService { + private final Storage storage = Storage.getInstance(); + private final UserService userService = new UserService(new UserRepository(storage)); + private final QuestionService questionService = new QuestionService(new QuestionRepository(storage)); + private final AnswerRepository answerRepository = new AnswerRepository(storage); + private final Repository gameRepository; + + public GameService(Repository gameRepository) { + this.gameRepository = gameRepository; + } + + public Optional get(Long id){ + return gameRepository.get(id); + } + + public Optional find(String questName, String userName){ + return gameRepository.findBy(questName, userName); + } + + public void post(Game game){ + game.setId(0L); + gameRepository.create(game); + } + + public void delete(Game game){ + gameRepository.delete(game); + } + + public void update(Game game){ + gameRepository.update(game); + } + + public Map getAll(){ + return gameRepository.getAll(); + } +} diff --git a/src/main/webapp/WEB-INF/parts/header.jsp b/src/main/webapp/WEB-INF/parts/header.jsp index 238d15c..95b123a 100644 --- a/src/main/webapp/WEB-INF/parts/header.jsp +++ b/src/main/webapp/WEB-INF/parts/header.jsp @@ -36,7 +36,7 @@