-
Notifications
You must be signed in to change notification settings - Fork 27
Project3# Quest final #3
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
b7ab8d0
8441136
dd2e5d4
114579c
f71cb47
a51dfe1
356a23c
9f428a5
dfbd98b
53ac90b
e37d4b2
e9d717f
e0115cd
6ea2aae
6c27950
1b69f54
d4f0a41
8cbe1bd
2f76683
5cb0f27
0498ecb
77028da
cfebd46
a05dcb3
f9fd3f5
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -5,6 +5,7 @@ target/ | |
|
|
||
| ### IntelliJ IDEA ### | ||
| .idea | ||
| *.iml | ||
|
|
||
|
|
||
| ### Eclipse ### | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,28 @@ | ||
| # Quest LedZeppelin | ||
|
|
||
| ### Ребята и девчата, всем привет! | ||
|
|
||
| На этот раз у нас более сложный проект, который будет трудно объединить в один общий проект со всем зависимостями. | ||
| Поэтому поступим просто. Тут первоначально лежит наш учебный проект. | ||
| Заготовка, такая же как может быть сгенерирована в 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. Поглядывайте периодически. | ||
|
|
||
| #### _2024 JRU LedZeppelin, Александр Хмелев._ |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,33 @@ | ||
| package com.javarush; | ||
|
|
||
| import java.time.LocalDateTime; | ||
|
|
||
| public class GameRecord { | ||
| private LocalDateTime timestamp; | ||
| private int score; | ||
| private boolean isWin; | ||
|
|
||
| public void setTimestamp(LocalDateTime timestamp) { | ||
| this.timestamp = timestamp; | ||
| } | ||
|
|
||
| public void setScore(int score) { | ||
| this.score = score; | ||
| } | ||
|
|
||
| public void setWin(boolean win) { | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Имя сеттера 'setWin' не совсем соответствует имени поля 'isWin'. Рекомендуется использовать 'setWon' или переименовать поле в 'win' для единообразия с 'isWin()'. |
||
| isWin = win; | ||
| } | ||
|
|
||
| public LocalDateTime getTimestamp() { | ||
| return timestamp; | ||
| } | ||
|
|
||
| public int getScore() { | ||
| return score; | ||
| } | ||
|
|
||
| public boolean isWin() { | ||
| return isWin; | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,23 +1,23 @@ | ||
| package com.javarush.khmelov.lesson14.controller; | ||
| package com.javarush.babkin; | ||
|
|
||
| import com.javarush.khmelov.lesson14.config.Winter; | ||
| import com.javarush.khmelov.lesson14.entity.Role; | ||
| import com.javarush.khmelov.lesson14.entity.User; | ||
| import com.javarush.khmelov.lesson14.service.UserService; | ||
| import com.javarush.babkin.entity.Role; | ||
| import com.javarush.babkin.entity.User; | ||
| import com.javarush.babkin.service.UserService; | ||
| import jakarta.servlet.ServletConfig; | ||
| import jakarta.servlet.ServletException; | ||
| import jakarta.servlet.annotation.WebServlet; | ||
| import jakarta.servlet.http.HttpServlet; | ||
| import jakarta.servlet.http.HttpServletRequest; | ||
| import jakarta.servlet.http.HttpServletResponse; | ||
|
|
||
|
|
||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Отсутствует валидация данных, поступающих из 'HttpServletRequest'. Это может привести к уязвимостям или ошибкам при обработке некорректного ввода. |
||
| import java.io.IOException; | ||
| import java.util.Optional; | ||
|
|
||
| @WebServlet(value = "/edit-user", loadOnStartup = 1) | ||
| public class EditUserServlet extends HttpServlet { | ||
|
|
||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Установка атрибута 'roles' в 'ServletContext' при каждом создании сервлета избыточна. Это лучше сделать один раз в 'ServletContextListener'. |
||
| private final UserService userService = Winter.find(UserService.class); | ||
| private final UserService userService = UserService.USER_SERVICE; | ||
|
|
||
| @Override | ||
| public void init(ServletConfig config) throws ServletException { | ||
|
|
@@ -26,30 +26,42 @@ public void init(ServletConfig config) throws ServletException { | |
|
|
||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Метод 'Long.parseLong(id)' может выбросить 'NumberFormatException', если параметр отсутствует или не является числом. Необходимо добавить проверку или блок try-catch. |
||
| @Override | ||
| protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Если пользователь с указанным ID не найден, сервлет продолжит выполнение без уведомления об ошибке. Следует предусмотреть обработку отсутствующего значения (например, редирект на 404). |
||
| String stringId = req.getParameter("id"); | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Закомментированный код ('// if (user.isPresent()) ...') следует удалять перед публикацией в репозиторий для поддержания чистоты проекта. |
||
| if (stringId != null) { | ||
| long id = Long.parseLong(stringId); | ||
| Optional<User> user = userService.get(id); | ||
| String id = req.getParameter("id"); | ||
| if (id != null) { | ||
| long userId = Long.parseLong(id); | ||
| Optional<User> user = userService.getById(userId); | ||
| user.ifPresent(value -> req.setAttribute("user", value)); | ||
| // if (user.isPresent()) { | ||
| // req.setAttribute("user", user); | ||
| // } | ||
| } | ||
| req.getRequestDispatcher("/WEB-INF/edit-user.jsp").forward(req, resp); | ||
|
|
||
| } | ||
|
|
||
| @Override | ||
| protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { | ||
| String parameter = req.getParameter("id"); | ||
| User user = User.builder() | ||
| .id(parameter!=null?Long.parseLong(parameter):null) | ||
| .id(parameter != null ? Long.parseLong(parameter) : null) | ||
| .login(req.getParameter("login")) | ||
| .password(req.getParameter("password")) | ||
| .role(Role.valueOf(req.getParameter("role"))) | ||
| .build(); | ||
| if (req.getParameter("create") != null) { | ||
| userService.create(user); | ||
| } else if (req.getParameter("update") != null) { | ||
| user.setId(Long.parseLong(req.getParameter("id"))); | ||
|
|
||
|
|
||
| // if (req.getParameter("create")!= null) { | ||
| // userService.create(user); | ||
| // } else | ||
| if (req.getParameter("update") != null) { | ||
| user.setId(Long.valueOf(req.getParameter("id"))); | ||
| userService.update(user); | ||
| } else if (req.getParameter("delete") != null) { | ||
| user.setId(Long.valueOf(req.getParameter("id"))); | ||
| userService.delete(user); | ||
| } | ||
|
|
||
| // resp.sendRedirect(req.getContextPath() + "/edit-user?id=" + req.getParameter("id")); | ||
| resp.sendRedirect(req.getContextPath() + "/edit-user?id=" + user.getId()); | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,42 @@ | ||
| package com.javarush.babkin; | ||
|
|
||
| public class PlayerStats { | ||
|
|
||
| private long playerId; | ||
| private int totalGames; | ||
| private int wins; | ||
| private int losses; | ||
|
|
||
| public PlayerStats(long playerId) { | ||
| this.playerId = playerId; | ||
| this.totalGames = 0; | ||
| this.wins = 0; | ||
| this.losses = 0; | ||
| } | ||
|
|
||
|
|
||
| public long getPlayerId() { return playerId; } | ||
| public int getTotalGames() { return totalGames; } | ||
| public int getWins() { return wins; } | ||
| public int getLosses() { return losses; } | ||
|
|
||
|
|
||
| public void setTotalGames(int totalGames) { this.totalGames = totalGames; } | ||
| public void setWins(int wins) { this.wins = wins; } | ||
| public void setLosses(int losses) { this.losses = losses; } | ||
|
|
||
| public double getWinRate() { | ||
| if (totalGames == 0) return 0.0; | ||
| return (double) wins / totalGames * 100; | ||
| } | ||
|
|
||
| public void incrementWin() { | ||
| totalGames++; | ||
| wins++; | ||
| } | ||
|
|
||
| public void incrementLoss() { | ||
| totalGames++; | ||
| losses++; | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,16 @@ | ||
| package com.javarush.babkin; | ||
|
|
||
| public class QuestProgress { | ||
| private String step = "start"; | ||
| private boolean finished = false; | ||
|
|
||
| public QuestProgress() { | ||
| this.step = "start"; | ||
| this.finished = false; | ||
| } | ||
|
|
||
| public String getStep() { return step; } | ||
| public void setStep(String step) { this.step = step; } | ||
| public boolean isFinished() { return finished; } | ||
| public void setFinished(boolean finished) { this.finished = finished; } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,43 @@ | ||
| package com.javarush.babkin; | ||
|
|
||
| public class UserDto { | ||
| private long id; | ||
| private String login; | ||
| private int totalGames; | ||
| private int wins; | ||
| private int losses; | ||
| private double winRate; | ||
|
|
||
| public UserDto(long id, String login, int totalGames, int wins, int losses, double winRate) { | ||
| this.id = id; | ||
| this.login = login; | ||
| this.totalGames = totalGames; | ||
| this.wins = wins; | ||
| this.losses = losses; | ||
| this.winRate = winRate; | ||
| } | ||
|
|
||
| public long getId() { | ||
| return id; | ||
| } | ||
|
|
||
| public String getLogin() { | ||
| return login; | ||
| } | ||
|
|
||
| public int getTotalGames() { | ||
| return totalGames; | ||
| } | ||
|
|
||
| public int getWins() { | ||
| return wins; | ||
| } | ||
|
|
||
| public int getLosses() { | ||
| return losses; | ||
| } | ||
|
|
||
| public double getWinRate() { | ||
| return winRate; | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,27 +1,28 @@ | ||
| package com.javarush.khmelov.lesson14.controller; | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. В проекте полностью отсутствует логирование (например, через SLF4J). Использование 'System.out.println' или отсутствие логов затрудняет отладку в продакшене. |
||
| package com.javarush.babkin; | ||
|
|
||
| import com.javarush.khmelov.lesson14.config.Winter; | ||
| import com.javarush.khmelov.lesson14.entity.User; | ||
| import com.javarush.khmelov.lesson14.service.UserService; | ||
| import com.javarush.babkin.entity.User; | ||
| import com.javarush.babkin.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; | ||
|
|
||
|
|
||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Использование пустой строки '""' в URL-паттерне может привести к конфликтам при обработке корневых запросов. Лучше использовать конкретный путь. |
||
| import java.io.IOException; | ||
| import java.util.Collection; | ||
|
|
||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Прямая зависимость от синглтона 'UserService.USER_SERVICE' затрудняет тестирование. Рекомендуется использовать внедрение зависимостей (Dependency Injection). |
||
| @WebServlet("/list-user") | ||
| public class ListUserServlet extends HttpServlet { | ||
| @WebServlet(value = {"","/user-list"}) | ||
| public class UserListServlet extends HttpServlet { | ||
|
|
||
| private final UserService userService= Winter.find(UserService.class); | ||
| private final UserService userService = UserService.USER_SERVICE; | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Путь к JSP-файлу жестко закодирован в методе. Подобные константы следует выносить в отдельные статические переменные или конфигурацию. |
||
|
|
||
| @Override | ||
| protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { | ||
| Collection<User> users = userService.getAll(); | ||
|
|
||
| req.setAttribute("users", users); | ||
| req.getRequestDispatcher("/WEB-INF/list-user.jsp") | ||
| .forward(req, resp); | ||
| req.getRequestDispatcher("/WEB-INF/user-list.jsp").forward(req, resp); | ||
|
|
||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,34 @@ | ||
| package com.javarush.babkin; | ||
|
|
||
| import com.javarush.babkin.entity.User; | ||
| import com.javarush.babkin.service.StatsService; | ||
| import com.javarush.babkin.service.UserService; | ||
|
|
||
| public class UserStatsAdapter { | ||
|
|
||
| private final UserService userService; | ||
| private final StatsService statsService; | ||
|
|
||
| public UserStatsAdapter(UserService userService, StatsService statsService) { | ||
| this.userService = userService; | ||
| this.statsService = statsService; | ||
| } | ||
|
|
||
| public UserDto getUserWithStats(long userId) { | ||
| User user = userService.getUserById(userId); | ||
| if (user == null) { | ||
| return null; | ||
| } | ||
|
|
||
| PlayerStats stats = statsService.getStatsForUser(user); | ||
|
|
||
| return new UserDto( | ||
| user.getId(), | ||
| user.getLogin(), | ||
| stats.getTotalGames(), | ||
| stats.getWins(), | ||
| stats.getLosses(), | ||
| stats.getWinRate() | ||
| ); | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,19 @@ | ||
| package com.javarush.babkin.entity; | ||
|
|
||
|
|
||
| import lombok.AllArgsConstructor; | ||
| import lombok.Builder; | ||
| import lombok.Data; | ||
| import lombok.NoArgsConstructor; | ||
|
|
||
| @Data | ||
| @Builder | ||
| @NoArgsConstructor | ||
| @AllArgsConstructor | ||
| public class Quest { | ||
| private Long id; | ||
| private String title; | ||
| private String description; | ||
|
|
||
|
|
||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,5 @@ | ||
| package com.javarush.babkin.entity; | ||
|
|
||
| public enum Role { | ||
| ADMIN, USER, GUEST | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Поля класса рекомендуется делать финальными, если объект не предполагает изменения после создания. Это обеспечивает потокобезопасность и упрощает логику.