From 260ec62d590d59e0c4058f1bcd98b8d5ba291a73 Mon Sep 17 00:00:00 2001 From: DenisStepanidenko Date: Wed, 24 Dec 2025 20:11:47 +0300 Subject: [PATCH 1/4] =?UTF-8?q?=D0=A1=D0=B4=D0=B5=D0=BB=D0=B0=D0=BD=D0=B0?= =?UTF-8?q?=20=D1=80=D0=B5=D0=B0=D0=BB=D0=B8=D0=B7=D0=B0=D1=86=D0=B8=D1=8F?= =?UTF-8?q?=20=D0=BF=D1=80=D0=BE=D0=B5=D0=BA=D1=82=D0=B0,=20=D0=BC=D0=B0?= =?UTF-8?q?=D0=B9=D0=BB=D1=81=D1=82=D0=BE=D1=83=D0=BD=D0=B0,=20=D1=82?= =?UTF-8?q?=D0=B8=D0=BA=D0=B5=D1=82=D0=B0.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 1 + .idea/.gitignore | 8 + .idea/.name | 1 + .idea/gradle.xml | 18 + .idea/misc.xml | 8 + .idea/uiDesigner.xml | 124 +++++++ .idea/vcs.xml | 6 + build.gradle | 65 ++++ build.gradle.kts | 20 -- settings.gradle.kts => settings.gradle | 0 src/main/java/org/lab/Main.java | 18 +- .../org/lab/model/milestone/Milestone.java | 48 +++ .../lab/model/milestone/MilestoneStatus.java | 7 + .../org/lab/model/permission/Permission.java | 324 ++++++++++++++++++ .../java/org/lab/model/project/Project.java | 191 +++++++++++ .../java/org/lab/model/role/Developer.java | 10 + src/main/java/org/lab/model/role/Manager.java | 10 + src/main/java/org/lab/model/role/QA.java | 10 + src/main/java/org/lab/model/role/Role.java | 25 ++ .../java/org/lab/model/role/TeamLead.java | 10 + .../java/org/lab/model/ticket/Ticket.java | 116 +++++++ .../org/lab/model/ticket/TicketStatus.java | 7 + src/main/java/org/lab/model/user/User.java | 84 +++++ .../java/org/lab/service/UserService.java | 31 ++ 24 files changed, 1121 insertions(+), 21 deletions(-) create mode 100644 .idea/.gitignore create mode 100644 .idea/.name create mode 100644 .idea/gradle.xml create mode 100644 .idea/misc.xml create mode 100644 .idea/uiDesigner.xml create mode 100644 .idea/vcs.xml create mode 100644 build.gradle delete mode 100644 build.gradle.kts rename settings.gradle.kts => settings.gradle (100%) create mode 100644 src/main/java/org/lab/model/milestone/Milestone.java create mode 100644 src/main/java/org/lab/model/milestone/MilestoneStatus.java create mode 100644 src/main/java/org/lab/model/permission/Permission.java create mode 100644 src/main/java/org/lab/model/project/Project.java create mode 100644 src/main/java/org/lab/model/role/Developer.java create mode 100644 src/main/java/org/lab/model/role/Manager.java create mode 100644 src/main/java/org/lab/model/role/QA.java create mode 100644 src/main/java/org/lab/model/role/Role.java create mode 100644 src/main/java/org/lab/model/role/TeamLead.java create mode 100644 src/main/java/org/lab/model/ticket/Ticket.java create mode 100644 src/main/java/org/lab/model/ticket/TicketStatus.java create mode 100644 src/main/java/org/lab/model/user/User.java create mode 100644 src/main/java/org/lab/service/UserService.java diff --git a/.gitignore b/.gitignore index b63da45..5f90047 100644 --- a/.gitignore +++ b/.gitignore @@ -5,6 +5,7 @@ build/ !**/src/test/**/build/ ### IntelliJ IDEA ### +./.idea .idea/modules.xml .idea/jarRepositories.xml .idea/compiler.xml diff --git a/.idea/.gitignore b/.idea/.gitignore new file mode 100644 index 0000000..13566b8 --- /dev/null +++ b/.idea/.gitignore @@ -0,0 +1,8 @@ +# Default ignored files +/shelf/ +/workspace.xml +# Editor-based HTTP Client requests +/httpRequests/ +# Datasource local storage ignored files +/dataSources/ +/dataSources.local.xml diff --git a/.idea/.name b/.idea/.name new file mode 100644 index 0000000..0194542 --- /dev/null +++ b/.idea/.name @@ -0,0 +1 @@ +features \ No newline at end of file diff --git a/.idea/gradle.xml b/.idea/gradle.xml new file mode 100644 index 0000000..d7be335 --- /dev/null +++ b/.idea/gradle.xml @@ -0,0 +1,18 @@ + + + + + + + \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml new file mode 100644 index 0000000..7026e02 --- /dev/null +++ b/.idea/misc.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/.idea/uiDesigner.xml b/.idea/uiDesigner.xml new file mode 100644 index 0000000..2b63946 --- /dev/null +++ b/.idea/uiDesigner.xml @@ -0,0 +1,124 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml new file mode 100644 index 0000000..35eb1dd --- /dev/null +++ b/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/build.gradle b/build.gradle new file mode 100644 index 0000000..f6aa8b6 --- /dev/null +++ b/build.gradle @@ -0,0 +1,65 @@ +plugins { + id 'java' + id 'application' +} + +group = 'org.lab' +version = '1.0-SNAPSHOT' + + +repositories { + mavenCentral() +} + +dependencies { + + + testImplementation platform('org.junit:junit-bom:5.10.0') + testImplementation 'org.junit.jupiter:junit-jupiter' + testRuntimeOnly 'org.junit.platform:junit-platform-launcher' +} + +application { + mainClass = 'org.lab.Main' + + applicationDefaultJvmArgs = [ + '-Dfile.encoding=UTF-8', + '-Dsun.stdout.encoding=UTF-8', + '-Dsun.stderr.encoding=UTF-8', + '-Dconsole.encoding=UTF-8', + ] +} + +java { + toolchain { + languageVersion = JavaLanguageVersion.of(24) + } +} + +tasks.withType(JavaCompile).configureEach { + options.compilerArgs += ['--enable-preview', '--release', '24'] +} + +tasks.withType(JavaExec).configureEach { + jvmArgs += '--enable-preview' +} + +tasks.withType(Test).configureEach { + jvmArgs += '--enable-preview' +} + +test { + useJUnitPlatform() +} + +tasks.withType(JavaExec).configureEach { + jvmArgs += [ + '-Dfile.encoding=UTF-8', + '-Dsun.stdout.encoding=UTF-8', + '-Dsun.stderr.encoding=UTF-8', + '--enable-preview' + ] + + // Для Windows консоли + systemProperty 'file.encoding', 'UTF-8' +} \ No newline at end of file diff --git a/build.gradle.kts b/build.gradle.kts deleted file mode 100644 index 79bf52a..0000000 --- a/build.gradle.kts +++ /dev/null @@ -1,20 +0,0 @@ -plugins { - id("java") -} - -group = "org.lab" -version = "1.0-SNAPSHOT" - -repositories { - mavenCentral() -} - -dependencies { - testImplementation(platform("org.junit:junit-bom:5.10.0")) - testImplementation("org.junit.jupiter:junit-jupiter") - testRuntimeOnly("org.junit.platform:junit-platform-launcher") -} - -tasks.test { - useJUnitPlatform() -} \ No newline at end of file diff --git a/settings.gradle.kts b/settings.gradle similarity index 100% rename from settings.gradle.kts rename to settings.gradle diff --git a/src/main/java/org/lab/Main.java b/src/main/java/org/lab/Main.java index 22028ef..363cd73 100644 --- a/src/main/java/org/lab/Main.java +++ b/src/main/java/org/lab/Main.java @@ -1,4 +1,20 @@ +import org.lab.model.project.Project; +import org.lab.model.user.User; +import org.lab.service.UserService; + +import java.io.IO; + void main() { - IO.println("Hello and welcome!"); + + UserService userService = new UserService(); + + User user = new User("Denis Stepanidenko"); + User teamLead = new User("Ilya"); + + Project project = Project.create(user); + project.attachTeamLead(teamLead, user); + + System.out.println(); + } diff --git a/src/main/java/org/lab/model/milestone/Milestone.java b/src/main/java/org/lab/model/milestone/Milestone.java new file mode 100644 index 0000000..4f274bb --- /dev/null +++ b/src/main/java/org/lab/model/milestone/Milestone.java @@ -0,0 +1,48 @@ +package org.lab.model.milestone; + +import org.lab.model.ticket.Ticket; + +import java.time.LocalDate; +import java.util.ArrayList; +import java.util.List; +import java.util.UUID; + +public class Milestone { + + private String milestoneId; + + private String projectId; + + private LocalDate startDate; + + private LocalDate endDate; + + private MilestoneStatus status; + + private List tickets = new ArrayList<>(); + + public Milestone(String projectId, LocalDate startDate, LocalDate endDate) { + milestoneId = UUID.randomUUID().toString(); + this.projectId = projectId; + this.startDate = startDate; + this.endDate = endDate; + status = MilestoneStatus.OPEN; + } + + public MilestoneStatus getStatus() { + return status; + } + + public void setStatus(MilestoneStatus status) { + this.status = status; + } + + public String getMilestoneId() { + return milestoneId; + } + + public List getTickets() { + return tickets; + } + +} diff --git a/src/main/java/org/lab/model/milestone/MilestoneStatus.java b/src/main/java/org/lab/model/milestone/MilestoneStatus.java new file mode 100644 index 0000000..cb1cc72 --- /dev/null +++ b/src/main/java/org/lab/model/milestone/MilestoneStatus.java @@ -0,0 +1,7 @@ +package org.lab.model.milestone; + +public enum MilestoneStatus { + + OPEN, ACTIVE, CLOSE + +} diff --git a/src/main/java/org/lab/model/permission/Permission.java b/src/main/java/org/lab/model/permission/Permission.java new file mode 100644 index 0000000..63c657c --- /dev/null +++ b/src/main/java/org/lab/model/permission/Permission.java @@ -0,0 +1,324 @@ +package org.lab.model.permission; + +public enum Permission { + + CREATE_PROJECT() { + @Override + public boolean allowedForManager() { + return true; + } + + @Override + public boolean allowedForTeamLead() { + return false; + } + + @Override + public boolean allowedForDeveloper() { + return false; + } + + @Override + public boolean allowedForQA() { + return false; + } + + }, + + ASSIGN_TEAMLEAD() { + @Override + public boolean allowedForManager() { + return true; + } + + @Override + public boolean allowedForTeamLead() { + return false; + } + + @Override + public boolean allowedForDeveloper() { + return false; + } + + @Override + public boolean allowedForQA() { + return false; + } + }, + + ASSIGN_DEVELOPER() { + @Override + public boolean allowedForManager() { + return true; + } + + @Override + public boolean allowedForTeamLead() { + return false; + } + + @Override + public boolean allowedForDeveloper() { + return false; + } + + @Override + public boolean allowedForQA() { + return false; + } + }, + + ASSIGN_QA() { + @Override + public boolean allowedForManager() { + return true; + } + + @Override + public boolean allowedForTeamLead() { + return false; + } + + @Override + public boolean allowedForDeveloper() { + return false; + } + + @Override + public boolean allowedForQA() { + return false; + } + }, + + CREATE_MILESTONE() { + @Override + public boolean allowedForManager() { + return true; + } + + @Override + public boolean allowedForTeamLead() { + return false; + } + + @Override + public boolean allowedForDeveloper() { + return false; + } + + @Override + public boolean allowedForQA() { + return false; + } + }, + + CHANGE_STATUS_OF_MILESTONE() { + @Override + public boolean allowedForManager() { + return true; + } + + @Override + public boolean allowedForTeamLead() { + return false; + } + + @Override + public boolean allowedForDeveloper() { + return false; + } + + @Override + public boolean allowedForQA() { + return false; + } + }, + + CREATE_TICKET() { + @Override + public boolean allowedForManager() { + return true; + } + + @Override + public boolean allowedForTeamLead() { + return true; + } + + @Override + public boolean allowedForDeveloper() { + return false; + } + + @Override + public boolean allowedForQA() { + return false; + } + }, + + ATTACH_DEVELOPER_TO_TICKET() { + @Override + public boolean allowedForManager() { + return true; + } + + @Override + public boolean allowedForTeamLead() { + return true; + } + + @Override + public boolean allowedForDeveloper() { + return false; + } + + @Override + public boolean allowedForQA() { + return false; + } + }, + + CHECK_TICKET() { + @Override + public boolean allowedForManager() { + return true; + } + + @Override + public boolean allowedForTeamLead() { + return true; + } + + @Override + public boolean allowedForDeveloper() { + return false; + } + + @Override + public boolean allowedForQA() { + return false; + } + + }, + + DO_TICKET() { + @Override + public boolean allowedForManager() { + return false; + } + + @Override + public boolean allowedForTeamLead() { + return false; + } + + @Override + public boolean allowedForDeveloper() { + return true; + } + + @Override + public boolean allowedForQA() { + return false; + } + }, + + CREATE_MESSAGE_ABOUT_MISTAKE() { + @Override + public boolean allowedForManager() { + return false; + } + + @Override + public boolean allowedForTeamLead() { + return false; + } + + @Override + public boolean allowedForDeveloper() { + return true; + } + + @Override + public boolean allowedForQA() { + return true; + } + }, + + FIXED_MESSAGE_ABOUT_MISTAKE() { + @Override + public boolean allowedForManager() { + return false; + } + + @Override + public boolean allowedForTeamLead() { + return false; + } + + @Override + public boolean allowedForDeveloper() { + return true; + } + + @Override + public boolean allowedForQA() { + return false; + } + }, + + TESTING_PROJECT() { + @Override + public boolean allowedForManager() { + return false; + } + + @Override + public boolean allowedForTeamLead() { + return false; + } + + @Override + public boolean allowedForDeveloper() { + return false; + } + + @Override + public boolean allowedForQA() { + return true; + } + }, + + CHECK_FIX() { + @Override + public boolean allowedForManager() { + return false; + } + + @Override + public boolean allowedForTeamLead() { + return false; + } + + @Override + public boolean allowedForDeveloper() { + return false; + } + + @Override + public boolean allowedForQA() { + return true; + } + }; + + + public abstract boolean allowedForManager(); + + public abstract boolean allowedForTeamLead(); + + public abstract boolean allowedForDeveloper(); + + public abstract boolean allowedForQA(); + +} diff --git a/src/main/java/org/lab/model/project/Project.java b/src/main/java/org/lab/model/project/Project.java new file mode 100644 index 0000000..f1685c1 --- /dev/null +++ b/src/main/java/org/lab/model/project/Project.java @@ -0,0 +1,191 @@ +package org.lab.model.project; + +import org.lab.model.milestone.Milestone; +import org.lab.model.milestone.MilestoneStatus; +import org.lab.model.role.Developer; +import org.lab.model.role.Manager; +import org.lab.model.role.QA; +import org.lab.model.role.TeamLead; +import org.lab.model.ticket.Ticket; +import org.lab.model.ticket.TicketStatus; +import org.lab.model.user.User; + +import java.time.LocalDate; +import java.util.*; + +public class Project { + + private String projectId; + + private User manager; + + private User teamLead; + + private Set developers = new HashSet<>(); + + private Set qa = new HashSet<>(); + + private LinkedList milestones = new LinkedList<>(); + + private Milestone currentMilestone; + + private Project() { + } + + /** + * Создание проекта пользователем. + * + * @param user пользователь, от имени которого создаётся проект (он будет менеджером) + */ + public static Project create(User user) { + + Project project = new Project(); + project.projectId = UUID.randomUUID().toString(); + project.manager = user; + user.addProject(project.projectId, new Manager()); + + return project; + } + + /** + * Добавление тимлида к проекту. + */ + public void attachTeamLead(User manager, User teamLead) { + + if (!manager.equals(this.manager)) { + + String errorMessage = String.format("User with name: %s and id: %s is not a manager of project with id: %s. Insufficient permissions to perform this action.", manager.getFullName(), manager.getId(), projectId); + System.out.println(errorMessage); + return; + } + + this.teamLead = teamLead; + teamLead.addProject(projectId, new TeamLead()); + + } + + /** + * Добавление разработчика к проекту. + */ + public void attachDeveloper(User manager, User developer) { + + if (!manager.equals(this.manager)) { + String errorMessage = String.format("User with name: %s and id: %s is not a manager of project with id: %s. Insufficient permissions to perform this action.", manager.getFullName(), manager.getId(), projectId); + System.out.println(errorMessage); + return; + } + + this.developers.add(developer); + developer.addProject(projectId, new Developer()); + + } + + /** + * Добавление тестировщика к проекту. + */ + public void attachQa(User manager, User qa) { + + if (!manager.equals(this.manager)) { + String errorMessage = String.format("User with name: %s and id: %s is not a manager of project with id: %s. Insufficient permissions to perform this action.", manager.getFullName(), manager.getId(), projectId); + System.out.println(errorMessage); + return; + } + + this.qa.add(qa); + qa.addProject(projectId, new QA()); + + } + + /** + * Создать новый Milestone. + * + * @param start начало milestone. + * @param finish окончание milestone. + */ + public void attachMilestone(User manager, LocalDate start, LocalDate finish) { + + if (!manager.equals(this.manager)) { + String errorMessage = String.format("User with name: %s and id: %s is not a manager of project with id: %s. Insufficient permissions to perform this action.", manager.getFullName(), manager.getId(), projectId); + System.out.println(errorMessage); + return; + } + + if (Objects.nonNull(currentMilestone)) { + System.out.println("The current milestone has not yet ended, so you cannot create a new one."); + return; + } + + Milestone milestone = new Milestone(projectId, start, finish); + currentMilestone = milestone; + milestones.push(milestone); + + } + + /** + * Изменить статус текущего milestone + */ + public void changeMilestoneStatus(User manager, MilestoneStatus newStatus) { + + if (!manager.equals(this.manager)) { + String errorMessage = String.format("User with name: %s and id: %s is not a manager of project with id: %s. Insufficient permissions to perform this action.", manager.getFullName(), manager.getId(), projectId); + System.out.println(errorMessage); + return; + } + + if (Objects.isNull(currentMilestone)) { + System.out.println("First, create the current milestone"); + return; + } + + + if (newStatus == MilestoneStatus.CLOSE) { + + for (Ticket ticket : currentMilestone.getTickets()) { + + if (ticket.getStatus() != TicketStatus.COMPLETED) { + + System.out.println("You cannot close the milestone because not all tickets have been completed."); + return; + } + + } + + currentMilestone.setStatus(newStatus); + currentMilestone = null; + return; + + } + + + currentMilestone.setStatus(newStatus); + + } + + /** + * Создание нового тикета (прикрепляется к текущему milestone) + */ + public Ticket addTicket(User user, String description) { + + if (!user.equals(this.manager) || !(Objects.nonNull(teamLead) && teamLead.equals(user))) { + String errorMessage = String.format("User with name: %s and id: %s is not a manager or teamLead of project with id: %s. Insufficient permissions to perform this action.", user.getFullName(), user.getId(), projectId); + System.out.println(errorMessage); + return null; + } + + if (Objects.isNull(currentMilestone)) { + System.out.println("First, create the current milestone"); + return null; + } + + Ticket ticket = new Ticket(this, currentMilestone, description, user); + + currentMilestone.getTickets().add(ticket); + + return ticket; + + } + + public Set getDevelopers() { + return developers; + } +} diff --git a/src/main/java/org/lab/model/role/Developer.java b/src/main/java/org/lab/model/role/Developer.java new file mode 100644 index 0000000..4baadc7 --- /dev/null +++ b/src/main/java/org/lab/model/role/Developer.java @@ -0,0 +1,10 @@ +package org.lab.model.role; + +public final class Developer implements Role { + + @Override + public String getRoleName() { + return "Developer"; + } + +} diff --git a/src/main/java/org/lab/model/role/Manager.java b/src/main/java/org/lab/model/role/Manager.java new file mode 100644 index 0000000..f028c5a --- /dev/null +++ b/src/main/java/org/lab/model/role/Manager.java @@ -0,0 +1,10 @@ +package org.lab.model.role; + +public final class Manager implements Role { + + @Override + public String getRoleName() { + return "Manager"; + } + +} diff --git a/src/main/java/org/lab/model/role/QA.java b/src/main/java/org/lab/model/role/QA.java new file mode 100644 index 0000000..5a74f06 --- /dev/null +++ b/src/main/java/org/lab/model/role/QA.java @@ -0,0 +1,10 @@ +package org.lab.model.role; + +public final class QA implements Role { + + @Override + public String getRoleName() { + return "QA"; + } + +} diff --git a/src/main/java/org/lab/model/role/Role.java b/src/main/java/org/lab/model/role/Role.java new file mode 100644 index 0000000..c37819a --- /dev/null +++ b/src/main/java/org/lab/model/role/Role.java @@ -0,0 +1,25 @@ +package org.lab.model.role; + + +import org.lab.model.permission.Permission; + +/** + * Роли участников проекта. + */ +public sealed interface Role permits Manager, Developer, TeamLead, QA { + + String getRoleName(); + + default boolean checkPermission(Permission permission) { + + return switch (this) { + case Manager _ -> permission.allowedForManager(); + case TeamLead _ -> permission.allowedForTeamLead(); + case Developer _ -> permission.allowedForDeveloper(); + case QA _ -> permission.allowedForQA(); + }; + + } + +} + diff --git a/src/main/java/org/lab/model/role/TeamLead.java b/src/main/java/org/lab/model/role/TeamLead.java new file mode 100644 index 0000000..18a728f --- /dev/null +++ b/src/main/java/org/lab/model/role/TeamLead.java @@ -0,0 +1,10 @@ +package org.lab.model.role; + +public final class TeamLead implements Role { + + @Override + public String getRoleName() { + return "TeamLead"; + } + +} diff --git a/src/main/java/org/lab/model/ticket/Ticket.java b/src/main/java/org/lab/model/ticket/Ticket.java new file mode 100644 index 0000000..df27c0f --- /dev/null +++ b/src/main/java/org/lab/model/ticket/Ticket.java @@ -0,0 +1,116 @@ +package org.lab.model.ticket; + +import org.lab.model.milestone.Milestone; +import org.lab.model.project.Project; +import org.lab.model.user.User; + +import java.util.*; + +public class Ticket { + + private String ticketId; + + private Project project; + + private Milestone milestone; + + private String description; + + private Set developers = new HashSet<>(); + + private int count = 0; + + private TicketStatus status; + + /** + * TeamLead или менеджер проекта, кто создал тикет. + */ + private User createdUser; + + public Ticket(Project project, Milestone milestone, String description, User createdUser) { + ticketId = UUID.randomUUID().toString(); + this.project = project; + this.milestone = milestone; + this.description = description; + this.createdUser = createdUser; + status = TicketStatus.NEW; + } + + /** + * Посмотреть статус тикета + */ + public TicketStatus getStatus() { + return status; + } + + /** + * Привязка разработчика к тикету. + */ + public void addDeveloper(User createdUser, User developer) { + + if (!createdUser.equals(this.createdUser)) { + + String errorMessage = "Insufficient permissions to perform this action."; + System.out.println(errorMessage); + return; + + } + + + if (!project.getDevelopers().contains(developer)) { + + String errorMessage = "The developer is not part of the project they are attached to this ticket."; + System.out.println(errorMessage); + return; + } + + + developers.add(developer); + status = TicketStatus.ACCEPTED; + count++; + } + + /** + * Разработчик выполняет задачу в тикете. + */ + public void activeTicket(User developer) { + + if (!developers.contains(developer)) { + String errorMessage = "Insufficient permissions to perform this action."; + System.out.println(errorMessage); + return; + } + + if (status == TicketStatus.COMPLETED) { + + String errorMessage = "The task in the ticket has already been completed."; + System.out.println(errorMessage); + return; + + } + + status = TicketStatus.ACTIVE; + + + } + + /** + * Разработчик выполнил задачу в тикете. + */ + public void finishTicket(User developer) { + + if (!developers.contains(developer)) { + String errorMessage = "Insufficient permissions to perform this action."; + System.out.println(errorMessage); + return; + } + + count--; + if (count == 0) { + status = TicketStatus.COMPLETED; + } + + } + + +} diff --git a/src/main/java/org/lab/model/ticket/TicketStatus.java b/src/main/java/org/lab/model/ticket/TicketStatus.java new file mode 100644 index 0000000..fdafac1 --- /dev/null +++ b/src/main/java/org/lab/model/ticket/TicketStatus.java @@ -0,0 +1,7 @@ +package org.lab.model.ticket; + +public enum TicketStatus { + + NEW, ACCEPTED, ACTIVE, COMPLETED + +} diff --git a/src/main/java/org/lab/model/user/User.java b/src/main/java/org/lab/model/user/User.java new file mode 100644 index 0000000..6c79d02 --- /dev/null +++ b/src/main/java/org/lab/model/user/User.java @@ -0,0 +1,84 @@ +package org.lab.model.user; + +import org.lab.model.role.Role; + +import java.util.*; + + +public class User { + + /** + * Уникальные идентификатор пользователя в системе. + */ + private String id; + + /** + * Полное имя пользователя. + */ + private String fullName; + + /** + * + * Список ролей в разных проектах. (допускается в одном проекте иметь несколько ролей) + */ + private Map> roles = new HashMap<>(); + + + public User(String fullName) { + this.fullName = fullName; + } + + + /** + * Назначить роль в проекте. + */ + public void addProject(String projectId, Role role) { + + Set currentRoles = roles.computeIfAbsent(projectId, k -> new HashSet<>()); + currentRoles.add(role); + + } + + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + + public String getFullName() { + return fullName; + } + + public void setFullName(String fullName) { + this.fullName = fullName; + } + + public Map> getRoles() { + return roles; + } + + public void setRoles(Map> roles) { + this.roles = roles; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + User user = (User) o; + return Objects.equals(id, user.id); + } + + @Override + public int hashCode() { + return Objects.hashCode(id); + } + +} + + + + + diff --git a/src/main/java/org/lab/service/UserService.java b/src/main/java/org/lab/service/UserService.java new file mode 100644 index 0000000..a12d4ec --- /dev/null +++ b/src/main/java/org/lab/service/UserService.java @@ -0,0 +1,31 @@ +package org.lab.service; + +import org.lab.model.user.User; + +import java.util.HashMap; +import java.util.Map; +import java.util.UUID; + +public class UserService { + + private Map users; + + + public UserService() { + users = new HashMap<>(); + } + + public User registerUser(String fullName) { + + String id = UUID.randomUUID().toString(); + User user = new User(fullName); + user.setId(id); + + users.put(id, user); + + return user; + + } + + +} From 589c6201db3648a071d363470939e19bed93f5a5 Mon Sep 17 00:00:00 2001 From: DenisStepanidenko Date: Thu, 25 Dec 2025 00:30:45 +0300 Subject: [PATCH 2/4] =?UTF-8?q?=D0=A1=D0=B4=D0=B5=D0=BB=D0=B0=D0=BD=D1=8B?= =?UTF-8?q?=20=D1=82=D0=B5=D1=81=D1=82=D1=8B,=20=D0=B4=D0=BE=D0=B1=D0=B0?= =?UTF-8?q?=D0=B2=D0=BB=D0=B5=D0=BD=D0=B0=20=D0=BB=D0=BE=D0=B3=D0=B8=D0=BA?= =?UTF-8?q?=D0=B0=20=D1=81=20bug-report?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/org/lab/Main.java | 105 +++++- .../org/lab/model/milestone/Milestone.java | 9 + .../org/lab/model/permission/Permission.java | 324 ------------------ .../java/org/lab/model/project/Project.java | 65 +++- .../java/org/lab/model/report/Report.java | 122 +++++++ .../org/lab/model/report/ReportStatus.java | 8 + src/main/java/org/lab/model/role/Role.java | 12 - .../java/org/lab/model/ticket/Ticket.java | 7 + src/main/java/org/lab/model/user/User.java | 93 ++++- .../java/org/lab/service/UserService.java | 1 - 10 files changed, 379 insertions(+), 367 deletions(-) delete mode 100644 src/main/java/org/lab/model/permission/Permission.java create mode 100644 src/main/java/org/lab/model/report/Report.java create mode 100644 src/main/java/org/lab/model/report/ReportStatus.java diff --git a/src/main/java/org/lab/Main.java b/src/main/java/org/lab/Main.java index 363cd73..9f20072 100644 --- a/src/main/java/org/lab/Main.java +++ b/src/main/java/org/lab/Main.java @@ -1,20 +1,115 @@ +import org.lab.model.milestone.MilestoneStatus; import org.lab.model.project.Project; +import org.lab.model.report.Report; +import org.lab.model.ticket.Ticket; import org.lab.model.user.User; import org.lab.service.UserService; import java.io.IO; +import java.lang.classfile.attribute.ModuleAttribute; +import java.time.LocalDate; void main() { UserService userService = new UserService(); + User manager = userService.registerUser("Manager"); - User user = new User("Denis Stepanidenko"); - User teamLead = new User("Ilya"); - Project project = Project.create(user); - project.attachTeamLead(teamLead, user); + // создание проекта + Project project1 = new Project(manager, "Valhalla"); - System.out.println(); + // создание команды + User developer1 = userService.registerUser("Developer1"); + User developer2 = userService.registerUser("Developer2"); + User developer3 = userService.registerUser("Developer3"); + + User qa1 = userService.registerUser("QA1"); + User qa2 = userService.registerUser("QA2"); + User qa3 = userService.registerUser("QA3"); + + User teamLead = userService.registerUser("TeamLead"); + + // назначение ролей + + project1.attachTeamLead(manager, teamLead); + + project1.attachDeveloper(manager, developer1); + project1.attachDeveloper(manager, developer2); + project1.attachDeveloper(manager, developer3); + + project1.attachQa(manager, qa1); + project1.attachQa(manager, qa2); + project1.attachQa(manager, qa3); + + // создание первого milestone + project1.attachMilestone(manager, LocalDate.now(), LocalDate.now().plusDays(15)); + project1.changeMilestoneStatus(manager, MilestoneStatus.ACTIVE); + + Ticket ticket1 = project1.addTicket(manager, "Ticket1"); + Ticket ticket2 = project1.addTicket(manager, "Ticket2"); + Ticket ticket3 = project1.addTicket(teamLead, "Ticket3"); + + ticket1.addDeveloper(manager, developer1); + ticket2.addDeveloper(manager, developer2); + ticket3.addDeveloper(teamLead, developer3); + + ticket1.activeTicket(developer1); + ticket2.activeTicket(developer2); + ticket3.activeTicket(developer3); + + ticket1.finishTicket(developer1); + ticket2.finishTicket(developer2); + ticket3.finishTicket(developer3); + + System.out.println("Status of ticket1: " + ticket1.getStatus()); + System.out.println("Status of ticket2: " + ticket2.getStatus()); + System.out.println("Status of ticket3: " + ticket3.getStatus()); + + + Report report1 = project1.addReport(qa1, developer1, "bug-report1"); + Report report2 = project1.addReport(qa2, developer2, "bug-report2"); + Report report3 = project1.addReport(qa3, developer3, "bug-report3"); + + report1.fixedReport(developer1); + report2.fixedReport(developer2); + report3.fixedReport(developer3); + + report1.checkReport(qa1); + report2.checkReport(qa2); + report3.checkReport(qa3); + + report1.closeReport(qa1); + report2.closeReport(qa2); + report3.closeReport(qa3); + + + // каждый может просмотреть все проекты, в которых он участвует + manager.viewAllProjects(); + teamLead.viewAllProjects(); + developer1.viewAllProjects(); + developer2.viewAllProjects(); + developer3.viewAllProjects(); + qa1.viewAllProjects(); + qa2.viewAllProjects(); + qa3.viewAllProjects(); + + manager.viewAllTasks(); + teamLead.viewAllTasks(); + developer1.viewAllTasks(); + developer2.viewAllTasks(); + developer3.viewAllTasks(); + qa1.viewAllTasks(); + qa2.viewAllTasks(); + qa3.viewAllTasks(); + + developer1.viewAllReport(); + developer2.viewAllReport(); + developer3.viewAllReport(); + + + + project1.changeMilestoneStatus(manager, MilestoneStatus.CLOSE); + System.out.println(project1.getMilestones()); } diff --git a/src/main/java/org/lab/model/milestone/Milestone.java b/src/main/java/org/lab/model/milestone/Milestone.java index 4f274bb..667fefe 100644 --- a/src/main/java/org/lab/model/milestone/Milestone.java +++ b/src/main/java/org/lab/model/milestone/Milestone.java @@ -45,4 +45,13 @@ public List getTickets() { return tickets; } + @Override + public String toString() { + return "Milestone{" + + "startDate=" + startDate + + ", milestoneId='" + milestoneId + '\'' + + ", endDate=" + endDate + + ", status=" + status + + '}'; + } } diff --git a/src/main/java/org/lab/model/permission/Permission.java b/src/main/java/org/lab/model/permission/Permission.java deleted file mode 100644 index 63c657c..0000000 --- a/src/main/java/org/lab/model/permission/Permission.java +++ /dev/null @@ -1,324 +0,0 @@ -package org.lab.model.permission; - -public enum Permission { - - CREATE_PROJECT() { - @Override - public boolean allowedForManager() { - return true; - } - - @Override - public boolean allowedForTeamLead() { - return false; - } - - @Override - public boolean allowedForDeveloper() { - return false; - } - - @Override - public boolean allowedForQA() { - return false; - } - - }, - - ASSIGN_TEAMLEAD() { - @Override - public boolean allowedForManager() { - return true; - } - - @Override - public boolean allowedForTeamLead() { - return false; - } - - @Override - public boolean allowedForDeveloper() { - return false; - } - - @Override - public boolean allowedForQA() { - return false; - } - }, - - ASSIGN_DEVELOPER() { - @Override - public boolean allowedForManager() { - return true; - } - - @Override - public boolean allowedForTeamLead() { - return false; - } - - @Override - public boolean allowedForDeveloper() { - return false; - } - - @Override - public boolean allowedForQA() { - return false; - } - }, - - ASSIGN_QA() { - @Override - public boolean allowedForManager() { - return true; - } - - @Override - public boolean allowedForTeamLead() { - return false; - } - - @Override - public boolean allowedForDeveloper() { - return false; - } - - @Override - public boolean allowedForQA() { - return false; - } - }, - - CREATE_MILESTONE() { - @Override - public boolean allowedForManager() { - return true; - } - - @Override - public boolean allowedForTeamLead() { - return false; - } - - @Override - public boolean allowedForDeveloper() { - return false; - } - - @Override - public boolean allowedForQA() { - return false; - } - }, - - CHANGE_STATUS_OF_MILESTONE() { - @Override - public boolean allowedForManager() { - return true; - } - - @Override - public boolean allowedForTeamLead() { - return false; - } - - @Override - public boolean allowedForDeveloper() { - return false; - } - - @Override - public boolean allowedForQA() { - return false; - } - }, - - CREATE_TICKET() { - @Override - public boolean allowedForManager() { - return true; - } - - @Override - public boolean allowedForTeamLead() { - return true; - } - - @Override - public boolean allowedForDeveloper() { - return false; - } - - @Override - public boolean allowedForQA() { - return false; - } - }, - - ATTACH_DEVELOPER_TO_TICKET() { - @Override - public boolean allowedForManager() { - return true; - } - - @Override - public boolean allowedForTeamLead() { - return true; - } - - @Override - public boolean allowedForDeveloper() { - return false; - } - - @Override - public boolean allowedForQA() { - return false; - } - }, - - CHECK_TICKET() { - @Override - public boolean allowedForManager() { - return true; - } - - @Override - public boolean allowedForTeamLead() { - return true; - } - - @Override - public boolean allowedForDeveloper() { - return false; - } - - @Override - public boolean allowedForQA() { - return false; - } - - }, - - DO_TICKET() { - @Override - public boolean allowedForManager() { - return false; - } - - @Override - public boolean allowedForTeamLead() { - return false; - } - - @Override - public boolean allowedForDeveloper() { - return true; - } - - @Override - public boolean allowedForQA() { - return false; - } - }, - - CREATE_MESSAGE_ABOUT_MISTAKE() { - @Override - public boolean allowedForManager() { - return false; - } - - @Override - public boolean allowedForTeamLead() { - return false; - } - - @Override - public boolean allowedForDeveloper() { - return true; - } - - @Override - public boolean allowedForQA() { - return true; - } - }, - - FIXED_MESSAGE_ABOUT_MISTAKE() { - @Override - public boolean allowedForManager() { - return false; - } - - @Override - public boolean allowedForTeamLead() { - return false; - } - - @Override - public boolean allowedForDeveloper() { - return true; - } - - @Override - public boolean allowedForQA() { - return false; - } - }, - - TESTING_PROJECT() { - @Override - public boolean allowedForManager() { - return false; - } - - @Override - public boolean allowedForTeamLead() { - return false; - } - - @Override - public boolean allowedForDeveloper() { - return false; - } - - @Override - public boolean allowedForQA() { - return true; - } - }, - - CHECK_FIX() { - @Override - public boolean allowedForManager() { - return false; - } - - @Override - public boolean allowedForTeamLead() { - return false; - } - - @Override - public boolean allowedForDeveloper() { - return false; - } - - @Override - public boolean allowedForQA() { - return true; - } - }; - - - public abstract boolean allowedForManager(); - - public abstract boolean allowedForTeamLead(); - - public abstract boolean allowedForDeveloper(); - - public abstract boolean allowedForQA(); - -} diff --git a/src/main/java/org/lab/model/project/Project.java b/src/main/java/org/lab/model/project/Project.java index f1685c1..b6350ca 100644 --- a/src/main/java/org/lab/model/project/Project.java +++ b/src/main/java/org/lab/model/project/Project.java @@ -2,6 +2,7 @@ import org.lab.model.milestone.Milestone; import org.lab.model.milestone.MilestoneStatus; +import org.lab.model.report.Report; import org.lab.model.role.Developer; import org.lab.model.role.Manager; import org.lab.model.role.QA; @@ -23,10 +24,16 @@ public class Project { private Set developers = new HashSet<>(); + private String description; + private Set qa = new HashSet<>(); private LinkedList milestones = new LinkedList<>(); + private List reports = new ArrayList<>(); + + + private Milestone currentMilestone; private Project() { @@ -37,14 +44,13 @@ private Project() { * * @param user пользователь, от имени которого создаётся проект (он будет менеджером) */ - public static Project create(User user) { + public Project(User user, String description) { - Project project = new Project(); - project.projectId = UUID.randomUUID().toString(); - project.manager = user; - user.addProject(project.projectId, new Manager()); + projectId = UUID.randomUUID().toString(); + manager = user; + this.description = description; + user.addProject(this, new Manager()); - return project; } /** @@ -60,7 +66,7 @@ public void attachTeamLead(User manager, User teamLead) { } this.teamLead = teamLead; - teamLead.addProject(projectId, new TeamLead()); + teamLead.addProject(this, new TeamLead()); } @@ -76,7 +82,7 @@ public void attachDeveloper(User manager, User developer) { } this.developers.add(developer); - developer.addProject(projectId, new Developer()); + developer.addProject(this, new Developer()); } @@ -92,7 +98,7 @@ public void attachQa(User manager, User qa) { } this.qa.add(qa); - qa.addProject(projectId, new QA()); + qa.addProject(this, new QA()); } @@ -166,7 +172,7 @@ public void changeMilestoneStatus(User manager, MilestoneStatus newStatus) { */ public Ticket addTicket(User user, String description) { - if (!user.equals(this.manager) || !(Objects.nonNull(teamLead) && teamLead.equals(user))) { + if (!user.equals(this.manager) && !(Objects.nonNull(teamLead) && teamLead.equals(user))) { String errorMessage = String.format("User with name: %s and id: %s is not a manager or teamLead of project with id: %s. Insufficient permissions to perform this action.", user.getFullName(), user.getId(), projectId); System.out.println(errorMessage); return null; @@ -185,7 +191,46 @@ public Ticket addTicket(User user, String description) { } + /** + * Здесь разработчик или тестировщик может создать bug-report + */ + public Report addReport(User createdUser, User fixedUser, String description) { + + Report report = new Report(description, createdUser, fixedUser, this); + reports.add(report); + + return report; + + } + + public Set getDevelopers() { return developers; } + + public String getDescription() { + return description; + } + + public LinkedList getMilestones() { + return milestones; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + Project project = (Project) o; + return Objects.equals(projectId, project.projectId); + } + + @Override + public int hashCode() { + return Objects.hashCode(projectId); + } + + public List getReports() { + return reports; + } + } diff --git a/src/main/java/org/lab/model/report/Report.java b/src/main/java/org/lab/model/report/Report.java new file mode 100644 index 0000000..abea2b5 --- /dev/null +++ b/src/main/java/org/lab/model/report/Report.java @@ -0,0 +1,122 @@ +package org.lab.model.report; + +import org.lab.model.project.Project; +import org.lab.model.user.User; + +import java.util.Objects; +import java.util.UUID; + +public class Report { + + private String reportId; + + private String description; + + /** + * Пользователь, кто создал сообщение об ошибки + */ + private User createdUser; + + /** + * Пользователь, кто исправил сообщение об ошибки + */ + private User fixedUser; + + private Project project; + + private ReportStatus status; + + public Report(String description, User createdUser, User fixedUser, Project project) { + reportId = UUID.randomUUID().toString(); + this.description = description; + this.createdUser = createdUser; + this.project = project; + this.fixedUser = fixedUser; + status = ReportStatus.NEW; + } + + + /** + * Разработчик устраняет сообщение об ошибке. + */ + public void fixedReport(User developer) { + + if (!project.getDevelopers().contains(developer)) { + + String errorMessage = "Insufficient permissions to perform this action."; + System.out.println(errorMessage); + return; + + } + + if (!fixedUser.equals(developer)) { + + String errorMessage = "Insufficient permissions to perform this action."; + System.out.println(errorMessage); + return; + + } + + status = ReportStatus.FIXED; + + } + + /** + * Тестировщик проверяет как исправил bug-report разработчик. + */ + public void checkReport(User user) { + + if (!user.equals(createdUser)) { + + String errorMessage = "Insufficient permissions to perform this action."; + System.out.println(errorMessage); + return; + } + + status = ReportStatus.TESTED; + + } + + /** + * Тестировщик закрывает bug-report + */ + public void closeReport(User user) { + + if (!user.equals(createdUser)) { + + String errorMessage = "Insufficient permissions to perform this action."; + System.out.println(errorMessage); + return; + } + + status = ReportStatus.CLOSED; + + } + + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + Report report = (Report) o; + return Objects.equals(reportId, report.reportId); + } + + @Override + public int hashCode() { + return Objects.hashCode(reportId); + } + + public User getCreatedUser() { + return createdUser; + } + + public String getDescription() { + return description; + } + + public User getFixedUser() { + return fixedUser; + } + +} diff --git a/src/main/java/org/lab/model/report/ReportStatus.java b/src/main/java/org/lab/model/report/ReportStatus.java new file mode 100644 index 0000000..1eaafd9 --- /dev/null +++ b/src/main/java/org/lab/model/report/ReportStatus.java @@ -0,0 +1,8 @@ +package org.lab.model.report; + +public enum ReportStatus { + + NEW, FIXED, TESTED, CLOSED + + +} diff --git a/src/main/java/org/lab/model/role/Role.java b/src/main/java/org/lab/model/role/Role.java index c37819a..47d6c30 100644 --- a/src/main/java/org/lab/model/role/Role.java +++ b/src/main/java/org/lab/model/role/Role.java @@ -1,7 +1,6 @@ package org.lab.model.role; -import org.lab.model.permission.Permission; /** * Роли участников проекта. @@ -10,16 +9,5 @@ public sealed interface Role permits Manager, Developer, TeamLead, QA { String getRoleName(); - default boolean checkPermission(Permission permission) { - - return switch (this) { - case Manager _ -> permission.allowedForManager(); - case TeamLead _ -> permission.allowedForTeamLead(); - case Developer _ -> permission.allowedForDeveloper(); - case QA _ -> permission.allowedForQA(); - }; - - } - } diff --git a/src/main/java/org/lab/model/ticket/Ticket.java b/src/main/java/org/lab/model/ticket/Ticket.java index df27c0f..68dfc92 100644 --- a/src/main/java/org/lab/model/ticket/Ticket.java +++ b/src/main/java/org/lab/model/ticket/Ticket.java @@ -43,6 +43,10 @@ public TicketStatus getStatus() { return status; } + public String getDescription() { + return description; + } + /** * Привязка разработчика к тикету. */ @@ -112,5 +116,8 @@ public void finishTicket(User developer) { } + public Set getDevelopers() { + return developers; + } } diff --git a/src/main/java/org/lab/model/user/User.java b/src/main/java/org/lab/model/user/User.java index 6c79d02..4278c58 100644 --- a/src/main/java/org/lab/model/user/User.java +++ b/src/main/java/org/lab/model/user/User.java @@ -1,8 +1,10 @@ package org.lab.model.user; +import org.lab.model.project.Project; import org.lab.model.role.Role; import java.util.*; +import java.util.stream.Collectors; public class User { @@ -21,7 +23,7 @@ public class User { * * Список ролей в разных проектах. (допускается в одном проекте иметь несколько ролей) */ - private Map> roles = new HashMap<>(); + private Map> roles = new HashMap<>(); public User(String fullName) { @@ -32,35 +34,96 @@ public User(String fullName) { /** * Назначить роль в проекте. */ - public void addProject(String projectId, Role role) { + public void addProject(Project project, Role role) { - Set currentRoles = roles.computeIfAbsent(projectId, k -> new HashSet<>()); + Set currentRoles = roles.computeIfAbsent(project, k -> new HashSet<>()); currentRoles.add(role); } - public String getId() { - return id; + /** + * Просмотреть все проекты + */ + public void viewAllProjects() { + + for (Map.Entry> entry : roles.entrySet()) { + + System.out.printf( + "Project: %s | Roles: %s%n", + entry.getKey().getDescription(), + entry.getValue().stream() + .map(Role::getRoleName) + .collect(Collectors.joining(", ")) + ); + + } + } - public void setId(String id) { - this.id = id; + /** + * Просмотреть все задачи + */ + public void viewAllTasks() { + + System.out.println("User: " + fullName); + + roles.forEach((project, _) -> { + System.out.println("Project: " + project.getDescription()); + + + project.getMilestones().forEach(milestone -> { + System.out.println("Milestone " + milestone.getMilestoneId()); + System.out.println("Tasks:"); + + milestone.getTickets().stream() + .filter(ticket -> ticket.getDevelopers().contains(this)) + .map(ticket -> "Description: " + ticket.getDescription()) + .forEach(System.out::println); + + System.out.println("--------------"); + }); + + + System.out.println("Reports"); + project.getReports().stream() + .filter(report -> report.getCreatedUser().equals(this)) + .map(report -> "Description: " + report.getDescription()) + .forEach(System.out::println); + + System.out.println("--------------"); + }); + + } - public String getFullName() { - return fullName; + public void viewAllReport() { + + System.out.println("User: " + fullName); + + roles.forEach((project, _) -> { + System.out.println("Project: " + project.getDescription()); + + System.out.println("Reports"); + project.getReports().stream() + .filter(report -> report.getFixedUser().equals(this)) + .map(report -> "Description: " + report.getDescription()) + .forEach(System.out::println); + + System.out.println("--------------"); + }); + } - public void setFullName(String fullName) { - this.fullName = fullName; + public String getId() { + return id; } - public Map> getRoles() { - return roles; + public void setId(String id) { + this.id = id; } - public void setRoles(Map> roles) { - this.roles = roles; + public String getFullName() { + return fullName; } @Override diff --git a/src/main/java/org/lab/service/UserService.java b/src/main/java/org/lab/service/UserService.java index a12d4ec..39ef954 100644 --- a/src/main/java/org/lab/service/UserService.java +++ b/src/main/java/org/lab/service/UserService.java @@ -10,7 +10,6 @@ public class UserService { private Map users; - public UserService() { users = new HashMap<>(); } From 1c7b4b170641be0c0bf362b4f1b758894858a9a1 Mon Sep 17 00:00:00 2001 From: DenisStepanidenko Date: Thu, 25 Dec 2025 14:44:00 +0300 Subject: [PATCH 3/4] =?UTF-8?q?=D0=94=D0=BE=D0=B1=D0=B0=D0=B2=D0=B8=D0=BB?= =?UTF-8?q?=20=D1=84=D0=B8=D1=87=D0=B8=20STR,=20record=20=D0=B8=20pattern?= =?UTF-8?q?=20matching?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .idea/misc.xml | 2 +- build.gradle | 7 +- src/main/java/org/lab/Main.java | 22 +++--- .../java/org/lab/model/project/Project.java | 68 ++++++++++++++++--- .../org/lab/model/project/ProjectStatus.java | 34 ++++++++++ .../java/org/lab/model/report/Report.java | 15 ++++ .../java/org/lab/model/ticket/Ticket.java | 21 ++++++ src/main/java/org/lab/model/user/User.java | 2 - 8 files changed, 145 insertions(+), 26 deletions(-) create mode 100644 src/main/java/org/lab/model/project/ProjectStatus.java diff --git a/.idea/misc.xml b/.idea/misc.xml index 7026e02..4b2a88c 100644 --- a/.idea/misc.xml +++ b/.idea/misc.xml @@ -4,5 +4,5 @@ - + \ No newline at end of file diff --git a/build.gradle b/build.gradle index f6aa8b6..5d5d1e3 100644 --- a/build.gradle +++ b/build.gradle @@ -20,7 +20,8 @@ dependencies { } application { - mainClass = 'org.lab.Main' + + applicationDefaultJvmArgs = [ '-Dfile.encoding=UTF-8', @@ -32,12 +33,12 @@ application { java { toolchain { - languageVersion = JavaLanguageVersion.of(24) + languageVersion = JavaLanguageVersion.of(21) } } tasks.withType(JavaCompile).configureEach { - options.compilerArgs += ['--enable-preview', '--release', '24'] + options.compilerArgs += ['-Xlint:preview', '--enable-preview'] } tasks.withType(JavaExec).configureEach { diff --git a/src/main/java/org/lab/Main.java b/src/main/java/org/lab/Main.java index 9f20072..8a5dbc7 100644 --- a/src/main/java/org/lab/Main.java +++ b/src/main/java/org/lab/Main.java @@ -1,3 +1,5 @@ + + import org.lab.model.milestone.MilestoneStatus; import org.lab.model.project.Project; import org.lab.model.report.Report; @@ -5,8 +7,8 @@ import org.lab.model.user.User; import org.lab.service.UserService; -import java.io.IO; -import java.lang.classfile.attribute.ModuleAttribute; + + import java.time.LocalDate; void main() { @@ -15,10 +17,10 @@ void main() { User manager = userService.registerUser("Manager"); - // создание проекта + Project project1 = new Project(manager, "Valhalla"); - // создание команды + User developer1 = userService.registerUser("Developer1"); User developer2 = userService.registerUser("Developer2"); User developer3 = userService.registerUser("Developer3"); @@ -29,7 +31,7 @@ void main() { User teamLead = userService.registerUser("TeamLead"); - // назначение ролей + project1.attachTeamLead(manager, teamLead); @@ -41,7 +43,7 @@ void main() { project1.attachQa(manager, qa2); project1.attachQa(manager, qa3); - // создание первого milestone + project1.attachMilestone(manager, LocalDate.now(), LocalDate.now().plusDays(15)); project1.changeMilestoneStatus(manager, MilestoneStatus.ACTIVE); @@ -49,6 +51,9 @@ void main() { Ticket ticket2 = project1.addTicket(manager, "Ticket2"); Ticket ticket3 = project1.addTicket(teamLead, "Ticket3"); + System.out.println(project1.getStats()); + + ticket1.addDeveloper(manager, developer1); ticket2.addDeveloper(manager, developer2); ticket3.addDeveloper(teamLead, developer3); @@ -61,9 +66,6 @@ void main() { ticket2.finishTicket(developer2); ticket3.finishTicket(developer3); - System.out.println("Status of ticket1: " + ticket1.getStatus()); - System.out.println("Status of ticket2: " + ticket2.getStatus()); - System.out.println("Status of ticket3: " + ticket3.getStatus()); Report report1 = project1.addReport(qa1, developer1, "bug-report1"); @@ -83,7 +85,7 @@ void main() { report3.closeReport(qa3); - // каждый может просмотреть все проекты, в которых он участвует + manager.viewAllProjects(); teamLead.viewAllProjects(); developer1.viewAllProjects(); diff --git a/src/main/java/org/lab/model/project/Project.java b/src/main/java/org/lab/model/project/Project.java index b6350ca..35f444e 100644 --- a/src/main/java/org/lab/model/project/Project.java +++ b/src/main/java/org/lab/model/project/Project.java @@ -33,12 +33,8 @@ public class Project { private List reports = new ArrayList<>(); - private Milestone currentMilestone; - private Project() { - } - /** * Создание проекта пользователем. * @@ -60,7 +56,12 @@ public void attachTeamLead(User manager, User teamLead) { if (!manager.equals(this.manager)) { - String errorMessage = String.format("User with name: %s and id: %s is not a manager of project with id: %s. Insufficient permissions to perform this action.", manager.getFullName(), manager.getId(), projectId); + String errorMessage = StringTemplate.STR.""" + User with name: \{manager.getFullName()} + and id: \{manager.getId()} + is not a manager of project with id: \{projectId}. + Insufficient permissions to perform this action. + """; System.out.println(errorMessage); return; } @@ -76,7 +77,12 @@ public void attachTeamLead(User manager, User teamLead) { public void attachDeveloper(User manager, User developer) { if (!manager.equals(this.manager)) { - String errorMessage = String.format("User with name: %s and id: %s is not a manager of project with id: %s. Insufficient permissions to perform this action.", manager.getFullName(), manager.getId(), projectId); + String errorMessage = StringTemplate.STR.""" + User with name: \{manager.getFullName()} + and id: \{manager.getId()} + is not a manager of project with id: \{projectId}. + Insufficient permissions to perform this action. + """; System.out.println(errorMessage); return; } @@ -92,7 +98,12 @@ public void attachDeveloper(User manager, User developer) { public void attachQa(User manager, User qa) { if (!manager.equals(this.manager)) { - String errorMessage = String.format("User with name: %s and id: %s is not a manager of project with id: %s. Insufficient permissions to perform this action.", manager.getFullName(), manager.getId(), projectId); + String errorMessage = StringTemplate.STR.""" + User with name: \{manager.getFullName()} + and id: \{manager.getId()} + is not a manager of project with id: \{projectId}. + Insufficient permissions to perform this action. + """; System.out.println(errorMessage); return; } @@ -111,7 +122,12 @@ public void attachQa(User manager, User qa) { public void attachMilestone(User manager, LocalDate start, LocalDate finish) { if (!manager.equals(this.manager)) { - String errorMessage = String.format("User with name: %s and id: %s is not a manager of project with id: %s. Insufficient permissions to perform this action.", manager.getFullName(), manager.getId(), projectId); + String errorMessage = StringTemplate.STR.""" + User with name: \{manager.getFullName()} + and id: \{manager.getId()} + is not a manager of project with id: \{projectId}. + Insufficient permissions to perform this action. + """; System.out.println(errorMessage); return; } @@ -133,7 +149,12 @@ public void attachMilestone(User manager, LocalDate start, LocalDate finish) { public void changeMilestoneStatus(User manager, MilestoneStatus newStatus) { if (!manager.equals(this.manager)) { - String errorMessage = String.format("User with name: %s and id: %s is not a manager of project with id: %s. Insufficient permissions to perform this action.", manager.getFullName(), manager.getId(), projectId); + String errorMessage = StringTemplate.STR.""" + User with name: \{manager.getFullName()} + and id: \{manager.getId()} + is not a manager of project with id: \{projectId}. + Insufficient permissions to perform this action. + """; System.out.println(errorMessage); return; } @@ -173,7 +194,12 @@ public void changeMilestoneStatus(User manager, MilestoneStatus newStatus) { public Ticket addTicket(User user, String description) { if (!user.equals(this.manager) && !(Objects.nonNull(teamLead) && teamLead.equals(user))) { - String errorMessage = String.format("User with name: %s and id: %s is not a manager or teamLead of project with id: %s. Insufficient permissions to perform this action.", user.getFullName(), user.getId(), projectId); + String errorMessage = StringTemplate.STR.""" + User with name: \{manager.getFullName()} + and id: \{manager.getId()} + is not a manager of project with id: \{projectId}. + Insufficient permissions to perform this action. + """; System.out.println(errorMessage); return null; } @@ -203,6 +229,28 @@ public Report addReport(User createdUser, User fixedUser, String description) { } + public ProjectStatus getStats() { + + int totalTickets = milestones.stream() + .mapToInt(m -> m.getTickets().size()) + .sum(); + + int completedTickets = milestones.stream() + .flatMap(m -> m.getTickets().stream()) + .filter(t -> t.getStatus() == TicketStatus.COMPLETED) + .toList() + .size(); + + return new ProjectStatus( + description, + totalTickets, + completedTickets, + totalTickets - completedTickets, + developers.size(), + qa.size() + ); + } + public Set getDevelopers() { return developers; diff --git a/src/main/java/org/lab/model/project/ProjectStatus.java b/src/main/java/org/lab/model/project/ProjectStatus.java new file mode 100644 index 0000000..80084c2 --- /dev/null +++ b/src/main/java/org/lab/model/project/ProjectStatus.java @@ -0,0 +1,34 @@ +package org.lab.model.project; + +public record ProjectStatus( + String projectName, + int totalTickets, + int completedTickets, + int openTickets, + int totalDevelopers, + int totalQA +) { + + public double completionPercentage() { + return totalTickets > 0 ? (completedTickets * 100.0) / totalTickets : 0.0; + } + + @Override + public String toString() { + return STR.""" + + PROJECT STATUS REPORT + Project: \{projectName} + + TICKETS STATISTICS: + Total tickets: \{totalTickets} + Completed: \{completedTickets} + Open: \{openTickets} + Completion rate: \{String.format("%.1f", completionPercentage())}% + TEAM COMPOSITION: + Developers: \{totalDevelopers} + QA Engineers: \{totalQA} + """; + } + +} \ No newline at end of file diff --git a/src/main/java/org/lab/model/report/Report.java b/src/main/java/org/lab/model/report/Report.java index abea2b5..5274ea5 100644 --- a/src/main/java/org/lab/model/report/Report.java +++ b/src/main/java/org/lab/model/report/Report.java @@ -119,4 +119,19 @@ public User getFixedUser() { return fixedUser; } + @Override + public String toString() { + return STR.""" + Report [ + ID: \{reportId} + Description: \{description} + Status: \{status} + Created by: \{createdUser.getFullName()} + Assigned to: \{fixedUser.getFullName()} + Project: \{project.getDescription()} + ] + """; + } + + } diff --git a/src/main/java/org/lab/model/ticket/Ticket.java b/src/main/java/org/lab/model/ticket/Ticket.java index 68dfc92..34ced7f 100644 --- a/src/main/java/org/lab/model/ticket/Ticket.java +++ b/src/main/java/org/lab/model/ticket/Ticket.java @@ -93,6 +93,7 @@ public void activeTicket(User developer) { } + status = TicketStatus.ACTIVE; @@ -116,8 +117,28 @@ public void finishTicket(User developer) { } + public String getStatusDescription() { + return switch(status) { + case NEW -> "New task"; + case ACCEPTED -> "Accepted for work"; + case ACTIVE -> "In progress"; + case COMPLETED -> "Completed"; + }; + } + public Set getDevelopers() { return developers; } + @Override + public String toString() { + return StringTemplate.STR.""" + Ticket: \{description} + Status: \{getStatusDescription()} + Developers: \{developers.size()} + Created by: \{createdUser.getFullName()} + """; + } + + } diff --git a/src/main/java/org/lab/model/user/User.java b/src/main/java/org/lab/model/user/User.java index 4278c58..84e95a9 100644 --- a/src/main/java/org/lab/model/user/User.java +++ b/src/main/java/org/lab/model/user/User.java @@ -77,7 +77,6 @@ public void viewAllTasks() { milestone.getTickets().stream() .filter(ticket -> ticket.getDevelopers().contains(this)) - .map(ticket -> "Description: " + ticket.getDescription()) .forEach(System.out::println); System.out.println("--------------"); @@ -87,7 +86,6 @@ public void viewAllTasks() { System.out.println("Reports"); project.getReports().stream() .filter(report -> report.getCreatedUser().equals(this)) - .map(report -> "Description: " + report.getDescription()) .forEach(System.out::println); System.out.println("--------------"); From 02dd7350f3c5205d727cffdb8e1e89e2c9883ab7 Mon Sep 17 00:00:00 2001 From: DenisStepanidenko Date: Thu, 25 Dec 2025 14:46:57 +0300 Subject: [PATCH 4/4] =?UTF-8?q?=D0=94=D0=BE=D0=B1=D0=B0=D0=B2=D0=B8=D0=BB?= =?UTF-8?q?=20=D1=84=D0=B8=D1=87=D0=B8=20STR,=20record=20=D0=B8=20pattern?= =?UTF-8?q?=20matching?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- build.gradle | 2 +- src/main/java/org/lab/Main.java | 2 +- src/main/java/org/lab/model/user/User.java | 21 ++++++++++----------- 3 files changed, 12 insertions(+), 13 deletions(-) diff --git a/build.gradle b/build.gradle index 5d5d1e3..c106d17 100644 --- a/build.gradle +++ b/build.gradle @@ -21,7 +21,7 @@ dependencies { application { - + mainClass = 'Main' applicationDefaultJvmArgs = [ '-Dfile.encoding=UTF-8', diff --git a/src/main/java/org/lab/Main.java b/src/main/java/org/lab/Main.java index 8a5dbc7..e4dafb9 100644 --- a/src/main/java/org/lab/Main.java +++ b/src/main/java/org/lab/Main.java @@ -85,7 +85,7 @@ void main() { report3.closeReport(qa3); - + manager.viewAllProjects(); teamLead.viewAllProjects(); developer1.viewAllProjects(); diff --git a/src/main/java/org/lab/model/user/User.java b/src/main/java/org/lab/model/user/User.java index 84e95a9..84ed437 100644 --- a/src/main/java/org/lab/model/user/User.java +++ b/src/main/java/org/lab/model/user/User.java @@ -46,17 +46,16 @@ public void addProject(Project project, Role role) { */ public void viewAllProjects() { - for (Map.Entry> entry : roles.entrySet()) { - - System.out.printf( - "Project: %s | Roles: %s%n", - entry.getKey().getDescription(), - entry.getValue().stream() - .map(Role::getRoleName) - .collect(Collectors.joining(", ")) - ); - - } + roles.forEach((project, roleSet) -> { + String rolesStr = roleSet.stream() + .map(Role::getRoleName) + .collect(Collectors.joining(", ")); + + System.out.println(STR.""" + Project: \{project.getDescription()} + Roles: \{rolesStr} + """); + }); }