Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
[![Review Assignment Due Date](https://classroom.github.com/assets/deadline-readme-button-22041afd0340ce965d47ae6ef1cefeee28c7c493a6346c4f15d667ab976d596c.svg)](https://classroom.github.com/a/TvkQWWs6)
# Features of modern Java

# Цели и задачи л/р:
Expand Down
10 changes: 10 additions & 0 deletions build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,16 @@ repositories {
mavenCentral()
}

java {
toolchain {
languageVersion = JavaLanguageVersion.of(25)
}
}

tasks.withType<JavaCompile>().configureEach {
options.compilerArgs.add("--enable-preview")
}

dependencies {
testImplementation(platform("org.junit:junit-bom:5.10.0"))
testImplementation("org.junit.jupiter:junit-jupiter")
Expand Down
146 changes: 144 additions & 2 deletions src/main/java/org/lab/Main.java
Original file line number Diff line number Diff line change
@@ -1,4 +1,146 @@
void main() {
IO.println("Hello and welcome!");
import org.lab.model.Project;
import org.lab.model.Ticket;
import org.lab.model.storage.Storage;
import org.lab.model.user.Developer;
import org.lab.model.user.Manager;
import org.lab.model.user.TeamLead;
import org.lab.model.user.Tester;
import org.lab.model.user.User;

void main() throws Exception {
var storage = new Storage();

var manager = new Manager(UUID.randomUUID());
var teamLead = new TeamLead(UUID.randomUUID());
var developer = new Developer(UUID.randomUUID());
var tester = new Tester(UUID.randomUUID());

storage.addUser(manager);
storage.addUser(teamLead);
storage.addUser(developer);
storage.addUser(tester);

IO.println("Пользователи созданы");

checkWhoCanCreateTickets(List.of(manager, teamLead, developer, tester), storage);

var project = new Project("Project Alpha", null, List.of(), List.of(), manager, null, List.of());
storage.addProject(project);

IO.println("Проект создан: " + project.name());

project = storage.setTeamLeadToProject(manager, project, teamLead);

IO.println("Тимлидер назначен: " + project.teamLead().id());

project = storage.addNewDeveloper(manager, project, developer);
project = storage.addNewTester(manager, project, tester);

IO.println("Разработчик и тестировщик добавлены");

var startDate = LocalDateTime.now();
var endDate = startDate.plusWeeks(2);

var milestone = storage.createMilestone("Milestone 1", "First development iteration", startDate, endDate);

project = storage.assignMilestoneToProject(manager, project, milestone);

IO.println("Майлстоун создан и назначен");

var ticket = storage.createTicket("Fix login bug", project, milestone);
var ticket2 = storage.createTicket("Fix SLA bug", project, milestone);
var ticket3 = storage.createTicket("Fix PostgreSQL bug", project, milestone);

milestone = storage.addTicketToMilestone(ticket, milestone);
milestone = storage.addTicketToMilestone(ticket2, milestone);
milestone = storage.addTicketToMilestone(ticket3, milestone);

project = project.withNewMilestone(milestone);

IO.println("Тикет создан: " + ticket.name());

ticket = storage.assignDeveloperToTicket(manager, ticket, developer);

IO.println("Разработчик назначен на тикет");

ticket = storage.startWorkingOnTicket(developer, ticket); // IN_PROGRESS

IO.println("Разработчик завершил тикет");

checkAllTicketsStatusAsync(project, storage);

ticket = storage.completeTicket(teamLead, ticket);

IO.println("Тикет подтвержден тимлидером");

var bug = storage.reportBug("Login crash", "Login crashes after 2nd attempt", project);

IO.println("Баг найден тестировщиком: " + bug.name());

bug = storage.fixBug(developer, bug);

IO.println("Баг исправлен разработчиком");

bug = storage.testBugFix(tester, bug);

IO.println("Баг проверен тестировщиком");

try {
milestone = milestone.close();
IO.println("Майлстоун успешно закрыт");
} catch (RuntimeException e) {
System.err.println("Не удалось закрыть майлстоун: " + e.getMessage());
}

System.out.printf("""
Финальный статус проекта:
Проект: %s
Майлстоун: %s (%s)
Тикеты: %d
Баги: %d
Статус бага: %s
%n""",
project.name(),
milestone.name(),
milestone.status(),
project.milestone().tickets().size(),
project.bugReports().size(),
bug.status()
);
}

private static void checkAllTicketsStatusAsync(Project project, Storage storage) throws Exception {
try (var scope = StructuredTaskScope.open()) {
for (var ticket : project.milestone().tickets()) {
scope.fork(() -> {
IO.println("Проверяю тикет: " + ticket.name() + " в потоке: " + Thread.currentThread());
if (ticket.status() == Ticket.Status.IN_PROGRESS) {
storage.startWorkingOnTicket(ticket.developers().getFirst(), ticket);
}
return null;
});
}
scope.join();

IO.println("Все тикеты проверены");
}
}

private static void checkWhoCanCreateTickets(List<User> users, Storage storage) {
try (var executor = Executors.newVirtualThreadPerTaskExecutor()) {
var futures = new ArrayList<Future<?>>();

for (var user : users) {
futures.add(executor.submit(() -> {
var canCreateTickets = storage.canCreateTicket(user);
IO.println("Пользователь " + user + " может создавать тикеты: " + canCreateTickets);
}));
}

for (var future : futures) {
future.get();
}
} catch (ExecutionException | InterruptedException e) {
throw new RuntimeException(e);
}
}
15 changes: 15 additions & 0 deletions src/main/java/org/lab/model/BugReport.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package org.lab.model;

import java.util.UUID;

public record BugReport(
UUID id,
String name,
String description,
Project project,
Status status
) {
public enum Status {
NEW, FIXED, TESTED, CLOSED
}
}
29 changes: 29 additions & 0 deletions src/main/java/org/lab/model/Milestone.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package org.lab.model;

import java.time.LocalDateTime;
import java.util.List;
import java.util.UUID;

public record Milestone(
UUID id,
String name,
String description,
Status status,
LocalDateTime startDate,
LocalDateTime endDate,
List<Ticket> tickets
) {
public enum Status {
OPEN, ACTIVE, CLOSED
}

public Milestone close() {
var allTicketsClosed = tickets.stream().allMatch(it -> it.status() == Ticket.Status.DONE);

if (!allTicketsClosed) {
throw new RuntimeException("Not all tickets are closed");
}

return new Milestone(id, name, description, Status.CLOSED, startDate, endDate, tickets);
}
}
27 changes: 27 additions & 0 deletions src/main/java/org/lab/model/Project.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package org.lab.model;

import org.lab.model.user.Developer;
import org.lab.model.user.Manager;
import org.lab.model.user.TeamLead;
import org.lab.model.user.Tester;

import java.util.List;

public record Project(
String name,
TeamLead teamLead,
List<Developer> developers,
List<Tester> testers,
Manager manager,
Milestone milestone,
List<BugReport> bugReports
) {

public Project withTeamLead(TeamLead teamLead) {
return new Project(name, teamLead, developers, testers, manager, milestone, bugReports);
}

public Project withNewMilestone(Milestone milestone) {
return new Project(name, teamLead, developers, testers, manager, milestone, bugReports);
}
}
20 changes: 20 additions & 0 deletions src/main/java/org/lab/model/Ticket.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package org.lab.model;

import org.lab.model.user.Developer;

import java.util.List;
import java.util.UUID;

public record Ticket(
UUID id,
String name,
Status status,
List<Developer> developers,
Project project,
Milestone milestone
) {

public enum Status {
NEW, ACCEPTED, IN_PROGRESS, DONE
}
}
Loading