From 29667844b230b8ec5f197f7475532f971403c06a Mon Sep 17 00:00:00 2001 From: Alexander Date: Sun, 20 Jul 2025 21:31:15 +0400 Subject: [PATCH 1/2] =?UTF-8?q?=D0=A0=D0=B5=D0=B0=D0=BB=D0=B8=D0=B7=D0=BE?= =?UTF-8?q?=D0=B2=D0=B0=D1=82=D1=8C:=20=D0=A2=D1=80=D0=B5=D0=BA=D0=B5?= =?UTF-8?q?=D1=80=20=D0=B7=D0=B0=D0=B4=D0=B0=D1=87=20=D0=B4=D0=BE=D0=BB?= =?UTF-8?q?=D0=B6=D0=B5=D0=BD=20=D1=80=D0=B0=D1=81=D1=81=D1=82=D0=B0=D0=B2?= =?UTF-8?q?=D0=BB=D1=8F=D1=82=D1=8C=20=D0=B7=D0=B0=D0=B4=D0=B0=D1=87=D0=B8?= =?UTF-8?q?=20=D0=BF=D0=BE=20=D0=BF=D1=80=D0=B8=D0=BE=D1=80=D0=B8=D1=82?= =?UTF-8?q?=D0=B5=D1=82=D1=83=20=D0=B8=20=D0=BF=D1=80=D0=BE=D0=B2=D0=B5?= =?UTF-8?q?=D1=80=D1=8F=D1=82=D1=8C,=20=D0=BD=D0=B5=20=D0=BF=D0=B5=D1=80?= =?UTF-8?q?=D0=B5=D1=81=D0=B5=D0=BA=D0=B0=D1=8E=D1=82=D1=81=D1=8F=20=D0=BB?= =?UTF-8?q?=D0=B8=20=D0=BE=D0=BD=D0=B8=20=D0=BF=D0=BE=20=D0=B2=D1=80=D0=B5?= =?UTF-8?q?=D0=BC=D0=B5=D0=BD=D0=B8=20=D0=B2=D1=8B=D0=BF=D0=BE=D0=BB=D0=BD?= =?UTF-8?q?=D0=B5=D0=BD=D0=B8=D1=8F.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- resources/data.csv | 7 +- src/Main.java | 75 +++++++- src/manager/CsvFormatter.java | 27 ++- src/manager/FileBackedTaskManager.java | 11 +- src/manager/InMemoryTaskManager.java | 164 ++++++++++++++---- src/manager/TaskManager.java | 15 ++ .../exceptions/ManagerSaveException.java | 5 + src/tasks/Epic.java | 22 ++- src/tasks/Subtask.java | 16 ++ src/tasks/Task.java | 48 +++++ test/manager/FileBackedTaskManagerTest.java | 32 ++++ test/manager/InMemoryTaskManagerTest.java | 93 ++++++++++ test/manager/TaskManagerTest.java | 1 + 13 files changed, 478 insertions(+), 38 deletions(-) diff --git a/resources/data.csv b/resources/data.csv index 5cdfc28..bb56863 100644 --- a/resources/data.csv +++ b/resources/data.csv @@ -1 +1,6 @@ -id,type,name,status,description,epic +id,type,name,status,description,epic, startTime, duration +5,TASK,Name,NEW,Desc,,2025-07-20T21:44:38.200815300,30 +9,TASK,Непересекающаяся,NEW,Описание,,2025-07-22T01:44:38.206666400,60 +6,EPIC,Отпуск,NEW,Организовать отпуск,,2025-07-21T20:44:38.206666400, +7,SUBTASK,Путёвка,NEW,Выбрать путёвку,6,2025-07-21T20:44:38.206666400,120 +8,SUBTASK,Билеты,NEW,Купить билеты,6,2025-07-21T23:44:38.206666400,60 diff --git a/src/Main.java b/src/Main.java index 8b6e991..81f3b33 100644 --- a/src/Main.java +++ b/src/Main.java @@ -1,12 +1,15 @@ import manager.FileBackedTaskManager; import manager.Managers; import manager.TaskManager; +import manager.exceptions.ManagerSaveException; import tasks.Task; import tasks.TaskStatus; import tasks.Subtask; import tasks.Epic; import java.io.File; +import java.time.Duration; +import java.time.LocalDateTime; public class Main { @@ -96,5 +99,75 @@ public static void main(String[] args) { System.out.println("Загруженные эпики: " + newLoadedManager.getEpics()); System.out.println("Загруженные сабтаски: " + newLoadedManager.getSubtasks()); System.out.println(); + + // 13. Создать таску с временными параметрами. + System.out.println("Создаем задачу с временным интервалом"); + LocalDateTime taskTime = LocalDateTime.now().plusHours(1); + Task task2 = new Task("Name", "Desc", TaskStatus.NEW, + taskTime, Duration.ofMinutes(30)); + taskManager.createTask(task2); + System.out.println("Задача создана: " + task2); + System.out.println("Время задачи: " + task2.getStartTime() + " - " + task2.getEndTime()); + System.out.println(); + + // 14. Проверка пересечения задач + System.out.println("Проверка запрета пересечения задач"); + Task overlappingTask = new Task("Пересекающаяся", "Описание", TaskStatus.NEW, + taskTime.plusMinutes(15), Duration.ofMinutes(30)); + try { + taskManager.createTask(overlappingTask); + System.err.println("ОШИБКА: Система разрешила создать пересекающуюся задачу!"); + } catch (ManagerSaveException e) { + System.out.println("КОРРЕКТНО: " + e.getMessage()); + System.out.println("Пересекающиеся задачи не добавлены - система работает правильно"); + } + System.out.println(); + + // 15. Создаём Эпик с подзадачами с временем. + System.out.println("Создаем эпик с временными подзадачами"); + Epic epic2 = new Epic("Отпуск", "Организовать отпуск"); + taskManager.createEpic(epic2); + + LocalDateTime subtask1Time = LocalDateTime.now().plusDays(1); + Subtask subtask3 = new Subtask("Путёвка", "Выбрать путёвку", TaskStatus.NEW, + epic2.getId(), subtask1Time, Duration.ofHours(2)); + + LocalDateTime subtask2Time = subtask1Time.plusHours(3); + Subtask subtask4 = new Subtask("Билеты", "Купить билеты", TaskStatus.NEW, + epic2.getId(), subtask2Time, Duration.ofHours(1)); + + taskManager.createSubtask(subtask3); + taskManager.createSubtask(subtask4); + + System.out.println("Время эпика: " + epic2.getStartTime() + " - " + epic2.getEndTime()); + System.out.println("Длительность эпика: " + + taskManager.getEpicDuration(epic2.getId()).toHours() + " часов"); + System.out.println(); + + // 16. Проверка пересечения с подзадачами + System.out.println("Проверка запрета пересечения с подзадачами"); + Task overlappingWithSubtask = new Task("Пересекается с подзадачей", "Описание", + TaskStatus.NEW, subtask1Time.plusMinutes(30), + Duration.ofHours(1)); + try { + taskManager.createTask(overlappingWithSubtask); + System.err.println("ОШИБКА: Система разрешила пересечение с подзадачей!"); + } catch (ManagerSaveException e) { + System.out.println("КОРРЕКТНО: " + e.getMessage()); + System.out.println("Пересечения с подзадачами запрещены - система работает правильно"); + } + System.out.println(); + + // 17. Проверка НЕпересекающихся задач + System.out.println("Проверка создания НЕпересекающихся задач"); + Task nonOverlappingTask = new Task("Непересекающаяся", "Описание", TaskStatus.NEW, + subtask1Time.plusHours(5), Duration.ofHours(1)); + try { + taskManager.createTask(nonOverlappingTask); + System.out.println("КОРРЕКТНО: Непересекающаяся задача создана успешно"); + System.out.println("Все задачи: " + taskManager.getTasks()); + } catch (ManagerSaveException e) { + System.err.println("ОШИБКА: " + e.getMessage()); + } } -} +} \ No newline at end of file diff --git a/src/manager/CsvFormatter.java b/src/manager/CsvFormatter.java index 6d33f14..4280716 100644 --- a/src/manager/CsvFormatter.java +++ b/src/manager/CsvFormatter.java @@ -1,9 +1,12 @@ package manager; import tasks.*; +import java.time.Duration; +import java.time.LocalDateTime; + public class CsvFormatter { - public static final String CSV_HEADER = "id,type,name,status,description,epic"; + public static final String CSV_HEADER = "id,type,name,status,description,epic, startTime, duration"; // Преобразование задачи в CSV строку @@ -15,13 +18,18 @@ public static String toString(Task task) { epicField = String.valueOf(((Subtask) task).getEpicId()); } + String startTime = task.getStartTime() != null ? task.getStartTime().toString() : ""; + String duration = task.getDuration() != null ? String.valueOf(task.getDuration().toMinutes()) : ""; + return String.join(",", String.valueOf(task.getId()), task.getType().name(), task.getName(), task.getTaskStatus().name(), task.getDescription(), - epicField); + epicField, + startTime, + duration); } // Создание задачи из CSV строки @@ -36,19 +44,30 @@ public static Task fromString(String value) { String name = fields[2]; TaskStatus status = TaskStatus.valueOf(fields[3]); String description = fields[4]; + String epicIdStr = fields.length > 5 ? fields[5] : ""; + String startTimeStr = fields.length > 6 ? fields[6] : ""; + String durationStr = fields.length > 7 ? fields[7] : ""; + + LocalDateTime startTime = !startTimeStr.isEmpty() ? LocalDateTime.parse(startTimeStr) : null; + Duration duration = !durationStr.isEmpty() ? Duration.ofMinutes(Long.parseLong(durationStr)) : null; switch (type) { case TASK: - return new Task(id, name, description, status); + Task task = new Task(id, name, description, status); + task.setStartTime(startTime); + task.setDuration(duration); + return task; case EPIC: Epic epic = new Epic(name, description); epic.setId(id); epic.setTaskStatus(status); return epic; case SUBTASK: - int epicId = fields.length > 5 ? Integer.parseInt(fields[5]) : 0; + int epicId = !epicIdStr.isEmpty() ? Integer.parseInt(epicIdStr) : 0; Subtask subtask = new Subtask(name, description, status, epicId); subtask.setId(id); + subtask.setStartTime(startTime); + subtask.setDuration(duration); return subtask; default: return null; diff --git a/src/manager/FileBackedTaskManager.java b/src/manager/FileBackedTaskManager.java index 29e8831..91561ee 100644 --- a/src/manager/FileBackedTaskManager.java +++ b/src/manager/FileBackedTaskManager.java @@ -12,7 +12,6 @@ public class FileBackedTaskManager extends InMemoryTaskManager { private final File file; - public FileBackedTaskManager(File file, HistoryManager historyManager) { this.file = file; this.historyManager = historyManager; @@ -164,6 +163,9 @@ private void load() { switch (task.getType()) { case TASK: tasks.put(task.getId(), task); + if (task.getStartTime() != null) { + getPrioritizedTasksSet().add(task); + } break; case EPIC: epics.put(task.getId(), (Epic) task); @@ -171,6 +173,9 @@ private void load() { case SUBTASK: Subtask subtask = (Subtask) task; subtasks.put(task.getId(), subtask); + if (subtask.getStartTime() != null) { + getPrioritizedTasksSet().add(subtask); + } Epic epic = epics.get(subtask.getEpicId()); if (epic != null) { epic.addSubtaskId(subtask.getId()); @@ -178,6 +183,10 @@ private void load() { break; } } + epics.values().forEach(epic -> { + super.updateEpicStatus(epic); // Явно вызываем родительский метод + super.updateEpicTime(epic); // Явно вызываем родительский метод + }); generatorId = maxId + 1; } catch (IOException e) { throw new ManagerLoadException("Не удалось загрузить задачи из файла: " + file.getPath(), e); diff --git a/src/manager/InMemoryTaskManager.java b/src/manager/InMemoryTaskManager.java index daf3737..6c8a0f8 100644 --- a/src/manager/InMemoryTaskManager.java +++ b/src/manager/InMemoryTaskManager.java @@ -1,15 +1,16 @@ package manager; +import manager.exceptions.ManagerSaveException; import tasks.Epic; import tasks.Subtask; import tasks.Task; import tasks.TaskStatus; - -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; +import java.time.Duration; +import java.time.LocalDateTime; +import java.util.Optional; import java.util.Objects; -import java.util.Map; +import java.util.*; + public class InMemoryTaskManager implements TaskManager { @@ -18,6 +19,17 @@ public class InMemoryTaskManager implements TaskManager { protected final Map epics = new HashMap<>(); protected int generatorId = 1; protected HistoryManager historyManager; + private final Set prioritizedTasks = new TreeSet<>((t1, t2) -> { + if (t1.getStartTime() == null && t2.getStartTime() == null) return t1.getId() - t2.getId(); + if (t1.getStartTime() == null) return 1; + if (t2.getStartTime() == null) return -1; + int timeCompare = t1.getStartTime().compareTo(t2.getStartTime()); + return timeCompare != 0 ? timeCompare : t1.getId() - t2.getId(); + }); + + protected Set getPrioritizedTasksSet() { + return prioritizedTasks; + } public InMemoryTaskManager() { this.historyManager = Managers.getDefaultHistory(); @@ -43,17 +55,21 @@ public Task getTask(int id) { @Override public Task createTask(Task task) { + if (hasTaskIntersections(task)) throw new ManagerSaveException("Задача пересекается по времени"); task.setId(getNextId()); tasks.put(task.getId(), task); + if (task.getStartTime() != null) prioritizedTasks.add(task); return task; } @Override public Task updateTask(Task task) { - if (!tasks.containsKey(task.getId())) { - return null; - } + if (!tasks.containsKey(task.getId())) return null; + if (hasTaskIntersections(task)) throw new ManagerSaveException("Задача пересекается по времени"); + Task oldTask = tasks.get(task.getId()); + prioritizedTasks.remove(oldTask); tasks.put(task.getId(), task); + if (task.getStartTime() != null) prioritizedTasks.add(task); return task; } @@ -61,6 +77,7 @@ public Task updateTask(Task task) { public Task deleteTask(int id) { Task task = tasks.remove(id); if (task != null) { + prioritizedTasks.remove(task); historyManager.remove(id); } return task; @@ -86,7 +103,7 @@ public Subtask createSubtask(Subtask subtask) { if (subtask == null || !epics.containsKey(subtask.getEpicId())) { return null; } - + if (hasTaskIntersections(subtask)) throw new ManagerSaveException("Подзадача пересекается по времени"); // Проверка для случая, когда ID подзадачи был установлен вручную if (subtask.getId() != null) { if (subtask.getId() == subtask.getEpicId()) { @@ -102,7 +119,8 @@ public Subtask createSubtask(Subtask subtask) { Epic epic = epics.get(subtask.getEpicId()); epic.addSubtaskId(subtask.getId()); updateEpicStatus(epic); - + updateEpicTime(epic); + if (subtask.getStartTime() != null) prioritizedTasks.add(subtask); return subtask; } @@ -111,11 +129,16 @@ public Subtask updateSubtask(Subtask subtask) { if (!subtasks.containsKey(subtask.getId())) { return null; } + if (hasTaskIntersections(subtask)) throw new ManagerSaveException("Подзадача пересекается по времени"); + Subtask oldSubtask = subtasks.get(subtask.getId()); + prioritizedTasks.remove(oldSubtask); subtasks.put(subtask.getId(), subtask); Epic epic = epics.get(subtask.getEpicId()); if (epic != null) { updateEpicStatus(epic); + updateEpicTime(epic); } + if (subtask.getStartTime() != null) prioritizedTasks.add(subtask); return subtask; } @@ -123,10 +146,12 @@ public Subtask updateSubtask(Subtask subtask) { public Subtask deleteSubtask(int id) { Subtask subtask = subtasks.remove(id); if (subtask != null) { + prioritizedTasks.remove(subtask); Epic epic = epics.get(subtask.getEpicId()); if (epic != null) { epic.removeSubtaskId(id); updateEpicStatus(epic); + updateEpicTime(epic); } historyManager.remove(id); } @@ -170,10 +195,10 @@ public Epic updateEpic(Epic epic) { public Epic deleteEpic(int id) { Epic epic = epics.remove(id); if (epic != null) { - for (Integer subtaskId : epic.getSubtasksId()) { + epic.getSubtasksId().forEach(subtaskId -> { subtasks.remove(subtaskId); historyManager.remove(subtaskId); - } + }); historyManager.remove(id); } return epic; @@ -186,41 +211,77 @@ public List getEpicSubtasks(int epicId) { return new ArrayList<>(); } List result = new ArrayList<>(); + epic.getSubtasksId().forEach(subtaskId -> { + Subtask subtask = subtasks.get(subtaskId); + if (subtask != null) result.add(subtask); + }); + return result; + } + + @Override + public Duration getEpicDuration(int epicId) { + Epic epic = epics.get(epicId); + if (epic == null || epic.getSubtasksId().isEmpty()) return Duration.ZERO; + + long totalMinutes = 0; for (Integer subtaskId : epic.getSubtasksId()) { Subtask subtask = subtasks.get(subtaskId); - if (subtask != null) { - result.add(subtask); + if (subtask != null && subtask.getDuration() != null) { + totalMinutes += subtask.getDuration().toMinutes(); } } - return result; + return Duration.ofMinutes(totalMinutes); } @Override - public void deleteAllTasks() { - for (Integer taskId : tasks.keySet()) { - historyManager.remove(taskId); + public LocalDateTime getEpicStartTime(int epicId) { + Epic epic = epics.get(epicId); + if (epic == null || epic.getSubtasksId().isEmpty()) return null; + + LocalDateTime earliest = null; + for (Integer subtaskId : epic.getSubtasksId()) { + Subtask subtask = subtasks.get(subtaskId); + if (subtask != null && subtask.getStartTime() != null) { + if (earliest == null || subtask.getStartTime().isBefore(earliest)) { + earliest = subtask.getStartTime(); + } + } } + return earliest; + } + + @Override + public LocalDateTime getEpicEndTime(int epicId) { + Epic epic = epics.get(epicId); + if (epic == null) return null; + return epic.getEndTime(); + } + + + + @Override + public void deleteAllTasks() { + tasks.keySet().forEach(taskId -> historyManager.remove(taskId)); + tasks.values().forEach(task -> prioritizedTasks.remove(task)); tasks.clear(); } @Override public void deleteAllSubtasks() { - for (Integer subtaskId : subtasks.keySet()) { - historyManager.remove(subtaskId); - } + subtasks.keySet().forEach(subtaskId -> historyManager.remove(subtaskId)); + subtasks.values().forEach(subtask -> prioritizedTasks.remove(subtask)); subtasks.clear(); - for (Epic epic : epics.values()) { + epics.values().forEach(epic -> { epic.getSubtasksId().clear(); updateEpicStatus(epic); - } + updateEpicTime(epic); + }); } @Override public void deleteAllEpics() { deleteAllSubtasks(); - for (Integer epicId : epics.keySet()) { - historyManager.remove(epicId); - } + epics.keySet().forEach(epicId -> historyManager.remove(epicId)); epics.clear(); } @@ -237,11 +298,52 @@ public List getHistory() { return historyManager.getHistory(); } - private void addHistory(Task task) { - historyManager.add(task); + @Override + public List getPrioritizedTasks() { + return new ArrayList<>(prioritizedTasks); + } + + @Override + public boolean isTasksIntersect(Task task1, Task task2) { + if (task1 == task2 || task1.getStartTime() == null || task2.getStartTime() == null + || task1.getEndTime() == null || task2.getEndTime() == null) { + return false; + } + return !task1.getStartTime().isAfter(task2.getEndTime()) && + !task2.getStartTime().isAfter(task1.getEndTime()); } - private void updateEpicStatus(Epic epic) { + @Override + public boolean hasTaskIntersections(Task newTask) { + if (newTask.getStartTime() == null) return false; + return getPrioritizedTasks().stream().anyMatch(existingTask -> + existingTask.getId() != newTask.getId() && + isTasksIntersect(existingTask, newTask) + ); + } + + protected void updateEpicTime(Epic epic) { + Optional startTime = epic.getSubtasksId().stream() + .map(id -> subtasks.get(id)) + .filter(subtask -> subtask != null) + .map(subtask -> subtask.getStartTime()) + .filter(time -> time != null) + .min((t1, t2) -> t1.compareTo(t2)); + + Optional endTime = epic.getSubtasksId().stream() + .map(id -> subtasks.get(id)) + .filter(subtask -> subtask != null) + .map(subtask -> subtask.getEndTime()) + .filter(time -> time != null) + .max((t1, t2) -> t1.compareTo(t2)); + + epic.setStartTime(startTime.orElse(null)); + epic.setEndTime(endTime.orElse(null)); + + } + + + protected void updateEpicStatus(Epic epic) { List subtaskIds = epic.getSubtasksId(); if (subtaskIds.isEmpty()) { epic.setTaskStatus(TaskStatus.NEW); @@ -273,6 +375,10 @@ private void updateEpicStatus(Epic epic) { private int getNextId() { return generatorId++; } + + private void addHistory(Task task) { + historyManager.add(task); + } } diff --git a/src/manager/TaskManager.java b/src/manager/TaskManager.java index 81e0619..08a5d52 100644 --- a/src/manager/TaskManager.java +++ b/src/manager/TaskManager.java @@ -3,6 +3,9 @@ import tasks.Epic; import tasks.Subtask; import tasks.Task; + +import java.time.Duration; +import java.time.LocalDateTime; import java.util.List; public interface TaskManager { @@ -52,4 +55,16 @@ public interface TaskManager { void deleteAllEpics(); void deleteAll(); + + Duration getEpicDuration(int epicId); + + LocalDateTime getEpicStartTime(int epicId); + + LocalDateTime getEpicEndTime(int epicId); + + List getPrioritizedTasks(); + + boolean isTasksIntersect(Task task1, Task task2); + + boolean hasTaskIntersections(Task newTask); } diff --git a/src/manager/exceptions/ManagerSaveException.java b/src/manager/exceptions/ManagerSaveException.java index 7352e06..7114253 100644 --- a/src/manager/exceptions/ManagerSaveException.java +++ b/src/manager/exceptions/ManagerSaveException.java @@ -1,6 +1,11 @@ package manager.exceptions; public class ManagerSaveException extends RuntimeException { + + public ManagerSaveException(String message) { + super(message); + } + public ManagerSaveException(String message, Throwable cause) { super(message, cause); } diff --git a/src/tasks/Epic.java b/src/tasks/Epic.java index e8fe0e3..3c13aec 100644 --- a/src/tasks/Epic.java +++ b/src/tasks/Epic.java @@ -1,27 +1,39 @@ package tasks; +import java.time.LocalDateTime; import java.util.ArrayList; public class Epic extends Task { private final ArrayList subtasksId = new ArrayList<>(); + private LocalDateTime endTime; public Epic(String name, String description) { super(name, description, TaskStatus.NEW); } public ArrayList getSubtasksId() { - return subtasksId; + return new ArrayList<>(subtasksId); } public void addSubtaskId(int subtaskId) { - subtasksId.add(subtaskId); + if (!subtasksId.contains(subtaskId)) { + subtasksId.add(subtaskId); + } } public void removeSubtaskId(int subtaskId) { subtasksId.remove((Integer) subtaskId); } + public void setEndTime(LocalDateTime endTime) { + this.endTime = endTime; + } + + public void clearSubtasksId() { + subtasksId.clear(); + } + @Override public TaskType getType() { return TaskType.EPIC; @@ -31,4 +43,10 @@ public TaskType getType() { public void setTaskStatus(TaskStatus taskStatus) { super.setTaskStatus(taskStatus); } + + @Override + public LocalDateTime getEndTime() { + return endTime; + } + } diff --git a/src/tasks/Subtask.java b/src/tasks/Subtask.java index df98a57..a4b8db1 100644 --- a/src/tasks/Subtask.java +++ b/src/tasks/Subtask.java @@ -1,5 +1,9 @@ package tasks; +import java.time.Duration; +import java.time.LocalDateTime; + + public class Subtask extends Task { private int epicId; @@ -14,6 +18,18 @@ public Subtask(Integer id, String name, String description, TaskStatus taskStatu this.epicId = epicId; } + public Subtask(String name, String description, TaskStatus taskStatus, int epicId, + LocalDateTime startTime, Duration duration) { + super(name, description, taskStatus, startTime, duration); + this.epicId = epicId; + } + + public Subtask(Integer id, String name, String description, TaskStatus taskStatus, int epicId, + LocalDateTime startTime, Duration duration) { + super(id, name, description, taskStatus, startTime, duration); + this.epicId = epicId; + } + @Override public TaskType getType() { return TaskType.SUBTASK; diff --git a/src/tasks/Task.java b/src/tasks/Task.java index 98ff3ba..20cebd7 100644 --- a/src/tasks/Task.java +++ b/src/tasks/Task.java @@ -1,5 +1,7 @@ package tasks; +import java.time.Duration; +import java.time.LocalDateTime; public class Task { @@ -7,6 +9,8 @@ public class Task { private String name; private String description; private TaskStatus taskStatus; + private Duration duration; + private LocalDateTime startTime; public Task(String name, String description, TaskStatus taskStatus) { @@ -15,6 +19,15 @@ public Task(String name, String description, TaskStatus taskStatus) { this.taskStatus = taskStatus; } + public Task(String name, String description, TaskStatus taskStatus, + LocalDateTime startTime, Duration duration) { + this.name = name; + this.description = description; + this.taskStatus = taskStatus; + this.startTime = startTime; + this.duration = duration; + } + public Task(Integer id, String name, String description, TaskStatus taskStatus) { this.id = id; this.name = name; @@ -22,6 +35,16 @@ public Task(Integer id, String name, String description, TaskStatus taskStatus) this.taskStatus = taskStatus; } + public Task(Integer id, String name, String description, TaskStatus taskStatus, LocalDateTime startTime, + Duration duration) { + this.id = id; + this.name = name; + this.description = description; + this.taskStatus = taskStatus; + this.startTime = startTime; + this.duration = duration; + } + public TaskType getType() { return TaskType.TASK; } @@ -58,6 +81,29 @@ public void setTaskStatus(TaskStatus taskStatus) { this.taskStatus = taskStatus; } + public LocalDateTime getStartTime() { + return startTime; + } + + public void setStartTime(LocalDateTime startTime) { + this.startTime = startTime; + } + + public Duration getDuration() { + return duration; + } + + public void setDuration(Duration duration) { + this.duration = duration; + } + + public LocalDateTime getEndTime() { + if (startTime == null || duration == null) { + return null; + } + return startTime.plus(duration); + } + @Override public String toString() { return "Task{" + @@ -65,6 +111,8 @@ public String toString() { ", name='" + name + '\'' + ", description='" + description + '\'' + ", taskStatus=" + taskStatus + + ", duration=" + duration + + ",startTime=" + startTime + '}'; } diff --git a/test/manager/FileBackedTaskManagerTest.java b/test/manager/FileBackedTaskManagerTest.java index 40a60fd..bf9d6e2 100644 --- a/test/manager/FileBackedTaskManagerTest.java +++ b/test/manager/FileBackedTaskManagerTest.java @@ -3,6 +3,10 @@ import java.io.File; import java.io.IOException; +import java.time.Duration; +import java.time.LocalDateTime; + +import manager.exceptions.ManagerSaveException; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.Test; import tasks.Epic; @@ -147,4 +151,32 @@ void testSaveAfterDeletingAllTasks() { assertNull(loadedManager.getTask(task2Id)); assertTrue(loadedManager.getTasks().isEmpty()); } + + // Проверка пересечения задач по времени в файловом менеджере + @Test + void savedTasksWithTimeIntersectionShouldThrowException() { + LocalDateTime startTime1 = LocalDateTime.now(); + Duration duration1 = Duration.ofHours(1); + Task task1 = new Task("Task 1", "Description", TaskStatus.NEW, startTime1, duration1); + taskManager.createTask(task1); + + LocalDateTime startTime2 = startTime1.plusMinutes(30); + Duration duration2 = Duration.ofHours(1); + Task task2 = new Task("Task 2", "Description", TaskStatus.NEW, startTime2, duration2); + + assertThrows(ManagerSaveException.class, () -> taskManager.createTask(task2), + "Должно быть выброшено исключение при пересечении задач по времени в файловом менеджере"); + } + + // Проверяем, что после загрузки из файла проверка пересечений работает + @Test + void loadedTasksShouldCheckTimeIntersections() { + LocalDateTime startTime = LocalDateTime.now(); + Task task1 = new Task("Task 1", "Description", TaskStatus.NEW, startTime, Duration.ofHours(1)); + taskManager.createTask(task1); + FileBackedTaskManager loadedManager = FileBackedTaskManager.loadFromFile(tempFile); + Task task2 = new Task("Task 2", "Description", TaskStatus.NEW, + startTime.plusMinutes(30), Duration.ofHours(1)); + assertThrows(ManagerSaveException.class, () -> loadedManager.createTask(task2)); + } } \ No newline at end of file diff --git a/test/manager/InMemoryTaskManagerTest.java b/test/manager/InMemoryTaskManagerTest.java index 7164838..5b94f17 100644 --- a/test/manager/InMemoryTaskManagerTest.java +++ b/test/manager/InMemoryTaskManagerTest.java @@ -1,9 +1,102 @@ package manager; +import manager.exceptions.ManagerSaveException; +import org.junit.jupiter.api.Test; +import tasks.Epic; +import tasks.Subtask; +import tasks.Task; +import tasks.TaskStatus; + +import java.time.Duration; +import java.time.LocalDateTime; + +import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; +import static org.junit.jupiter.api.Assertions.assertThrows; + public class InMemoryTaskManagerTest extends TaskManagerTest { @Override protected InMemoryTaskManager createTaskManager() { return new InMemoryTaskManager(); } + + // Тесты для проверки пересечения задач по времени. + @Test + void tasksWithTimeIntersectionShouldThrowException() { + LocalDateTime startTime1 = LocalDateTime.now(); + Duration duration1 = Duration.ofHours(1); + Task task1 = new Task("Task 1", "Description", TaskStatus.NEW, startTime1, duration1); + taskManager.createTask(task1); + + LocalDateTime startTime2 = startTime1.plusMinutes(30); + Duration duration2 = Duration.ofHours(1); + Task task2 = new Task("Task 2", "Description", TaskStatus.NEW, startTime2, duration2); + + assertThrows(ManagerSaveException.class, () -> taskManager.createTask(task2), + "Должно быть выброшено исключение при пересечении задач по времени"); + } + + @Test + void subtasksWithTimeIntersectionShouldThrowException() { + Epic epic = taskManager.createEpic(new Epic("Epic", "Description")); + + LocalDateTime startTime1 = LocalDateTime.now(); + Duration duration1 = Duration.ofHours(1); + Subtask subtask1 = new Subtask("Subtask 1", "Description", TaskStatus.NEW, + epic.getId(), startTime1, duration1); + taskManager.createSubtask(subtask1); + + LocalDateTime startTime2 = startTime1.plusMinutes(30); + Duration duration2 = Duration.ofHours(1); + Subtask subtask2 = new Subtask("Subtask 2", "Description", TaskStatus.NEW, + epic.getId(), startTime2, duration2); + + assertThrows(ManagerSaveException.class, () -> taskManager.createSubtask(subtask2), + "Должно быть выброшено исключение при пересечении подзадач по времени"); + } + + @Test + void tasksWithoutTimeShouldNotConflict() { + // Проверка задач без времени + Task task1 = new Task("Task 1", "Description", TaskStatus.NEW); + taskManager.createTask(task1); + + Task task2 = new Task("Task 2", "Description", TaskStatus.NEW); + + assertDoesNotThrow(() -> taskManager.createTask(task2), + "Задачи без времени выполнения не должны конфликтовать"); + } + + @Test + void tasksWithDifferentTimeShouldNotConflict() { + LocalDateTime startTime1 = LocalDateTime.now(); + Duration duration1 = Duration.ofHours(1); + Task task1 = new Task("Task 1", "Description", TaskStatus.NEW, startTime1, duration1); + taskManager.createTask(task1); + + LocalDateTime startTime2 = startTime1.plusHours(2); + Duration duration2 = Duration.ofHours(1); + Task task2 = new Task("Task 2", "Description", TaskStatus.NEW, startTime2, duration2); + + assertDoesNotThrow(() -> taskManager.createTask(task2), + "Задачи с разным временем выполнения не должны конфликтовать"); + } + + @Test + void updateTaskWithTimeIntersectionShouldThrowException() { + LocalDateTime startTime1 = LocalDateTime.now(); + Duration duration1 = Duration.ofHours(1); + Task task1 = new Task("Task 1", "Description", TaskStatus.NEW, startTime1, duration1); + taskManager.createTask(task1); + + LocalDateTime startTime2 = startTime1.plusHours(2); + Duration duration2 = Duration.ofHours(1); + Task task2 = new Task("Task 2", "Description", TaskStatus.NEW, startTime2, duration2); + taskManager.createTask(task2); + + task2.setStartTime(startTime1.plusMinutes(30)); + + assertThrows(ManagerSaveException.class, () -> taskManager.updateTask(task2), + "Должно быть выброшено исключение при обновлении задачи с пересечением времени"); + } } diff --git a/test/manager/TaskManagerTest.java b/test/manager/TaskManagerTest.java index 878d996..3f48663 100644 --- a/test/manager/TaskManagerTest.java +++ b/test/manager/TaskManagerTest.java @@ -1,6 +1,7 @@ package manager; import static org.junit.jupiter.api.Assertions.*; + import java.util.List; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; From 6761721bebde1719e8f261f840299feebe1f85c3 Mon Sep 17 00:00:00 2001 From: Alexander Date: Tue, 22 Jul 2025 20:27:30 +0400 Subject: [PATCH 2/2] =?UTF-8?q?=D0=98=D1=81=D0=BF=D1=80=D0=B0=D0=B2=D0=B8?= =?UTF-8?q?=D1=82=D1=8C=20=D0=B7=D0=B0=D0=BC=D0=B5=D1=87=D0=B0=D0=BD=D0=B8?= =?UTF-8?q?=D1=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- resources/data.csv | 10 +++++----- src/manager/FileBackedTaskManager.java | 4 ++-- src/manager/InMemoryTaskManager.java | 17 +++++++++++------ 3 files changed, 18 insertions(+), 13 deletions(-) diff --git a/resources/data.csv b/resources/data.csv index bb56863..377ec10 100644 --- a/resources/data.csv +++ b/resources/data.csv @@ -1,6 +1,6 @@ id,type,name,status,description,epic, startTime, duration -5,TASK,Name,NEW,Desc,,2025-07-20T21:44:38.200815300,30 -9,TASK,Непересекающаяся,NEW,Описание,,2025-07-22T01:44:38.206666400,60 -6,EPIC,Отпуск,NEW,Организовать отпуск,,2025-07-21T20:44:38.206666400, -7,SUBTASK,Путёвка,NEW,Выбрать путёвку,6,2025-07-21T20:44:38.206666400,120 -8,SUBTASK,Билеты,NEW,Купить билеты,6,2025-07-21T23:44:38.206666400,60 +5,TASK,Name,NEW,Desc,,2025-07-22T21:18:17.670284500,30 +9,TASK,Непересекающаяся,NEW,Описание,,2025-07-24T01:18:17.673142600,60 +6,EPIC,Отпуск,NEW,Организовать отпуск,,2025-07-23T20:18:17.673142600, +7,SUBTASK,Путёвка,NEW,Выбрать путёвку,6,2025-07-23T20:18:17.673142600,120 +8,SUBTASK,Билеты,NEW,Купить билеты,6,2025-07-23T23:18:17.673142600,60 diff --git a/src/manager/FileBackedTaskManager.java b/src/manager/FileBackedTaskManager.java index 91561ee..ae6a9bb 100644 --- a/src/manager/FileBackedTaskManager.java +++ b/src/manager/FileBackedTaskManager.java @@ -164,7 +164,7 @@ private void load() { case TASK: tasks.put(task.getId(), task); if (task.getStartTime() != null) { - getPrioritizedTasksSet().add(task); + addToPrioritizedTasks(task); } break; case EPIC: @@ -174,7 +174,7 @@ private void load() { Subtask subtask = (Subtask) task; subtasks.put(task.getId(), subtask); if (subtask.getStartTime() != null) { - getPrioritizedTasksSet().add(subtask); + addToPrioritizedTasks(task); } Epic epic = epics.get(subtask.getEpicId()); if (epic != null) { diff --git a/src/manager/InMemoryTaskManager.java b/src/manager/InMemoryTaskManager.java index 6c8a0f8..1900da5 100644 --- a/src/manager/InMemoryTaskManager.java +++ b/src/manager/InMemoryTaskManager.java @@ -27,8 +27,14 @@ public class InMemoryTaskManager implements TaskManager { return timeCompare != 0 ? timeCompare : t1.getId() - t2.getId(); }); + protected void addToPrioritizedTasks(Task task) { + if (task != null && task.getStartTime() != null) { + prioritizedTasks.add(task); + } + } + protected Set getPrioritizedTasksSet() { - return prioritizedTasks; + return new HashSet<>(prioritizedTasks); } public InMemoryTaskManager() { @@ -196,7 +202,10 @@ public Epic deleteEpic(int id) { Epic epic = epics.remove(id); if (epic != null) { epic.getSubtasksId().forEach(subtaskId -> { - subtasks.remove(subtaskId); + Subtask subtask = subtasks.remove(subtaskId); + if (subtask != null) { + prioritizedTasks.remove(subtask); + } historyManager.remove(subtaskId); }); historyManager.remove(id); @@ -375,10 +384,6 @@ protected void updateEpicStatus(Epic epic) { private int getNextId() { return generatorId++; } - - private void addHistory(Task task) { - historyManager.add(task); - } }