criar tasks, start e stop

This commit is contained in:
Gabriel Santana de Menezes 2026-04-29 21:11:43 -04:00
parent 090f2888d0
commit 2bb1ec97a9
24 changed files with 333 additions and 64 deletions

View file

@ -1,14 +1,17 @@
package net.beetechgroup.beetask.entities;
import java.time.Duration;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
public class Task {
private Long id;
private String title;
private String description;
private String status;
private TaskStatus status;
private String project;
private List<String> activities;
private List<TaskHistoryItem> history = new ArrayList<>();
public Long getId() {
return id;
@ -34,11 +37,11 @@ public class Task {
this.description = description;
}
public String getStatus() {
public TaskStatus getStatus() {
return status;
}
public void setStatus(String status) {
public void setStatus(TaskStatus status) {
this.status = status;
}
@ -50,12 +53,52 @@ public class Task {
this.project = project;
}
public List<String> getActivities() {
return activities;
public List<TaskHistoryItem> getHistory() {
return history;
}
public void setActivities(List<String> activities) {
this.activities = activities;
public void setHistory(List<TaskHistoryItem> history) {
this.history = history;
}
public boolean isRunning() {
return history.stream().anyMatch(TaskHistoryItem::isRunning);
}
private TaskHistoryItem getCurrentRunningItem() {
return history.stream()
.filter(TaskHistoryItem::isRunning)
.findFirst()
.orElse(null);
}
public void start() {
TaskHistoryItem current = getCurrentRunningItem();
if (current != null) {
throw new IllegalStateException("Tarefa já está em execução");
}
this.status = TaskStatus.IN_PROGRESS;
TaskHistoryItem item = new TaskHistoryItem();
item.setStartAt(LocalDateTime.now());
this.history.add(item);
}
public void stop() {
TaskHistoryItem current = getCurrentRunningItem();
if (current == null) {
throw new IllegalStateException("Task não está em execução");
}
current.setEndAt(LocalDateTime.now());
}
public Duration getTotalTime() {
return history.stream()
.map(TaskHistoryItem::getDuration)
.reduce(Duration.ZERO, Duration::plus);
}
}

View file

@ -0,0 +1,36 @@
package net.beetechgroup.beetask.entities;
import java.time.Duration;
import java.time.LocalDateTime;
public class TaskHistoryItem {
private LocalDateTime startAt;
private LocalDateTime endAt;
public void setEndAt(LocalDateTime endAt) {
this.endAt = endAt;
}
public LocalDateTime getStartAt() {
return startAt;
}
public void setStartAt(LocalDateTime startAt) {
this.startAt = startAt;
}
public LocalDateTime getEndAt() {
return endAt;
}
public Duration getDuration() {
if (endAt == null) {
return Duration.between(startAt, LocalDateTime.now());
}
return Duration.between(startAt, endAt);
}
public boolean isRunning() {
return endAt == null;
}
}

View file

@ -0,0 +1,18 @@
package net.beetechgroup.beetask.entities;
public enum TaskStatus {
NOT_STARTED("NOT_STARTED"),
IN_PROGRESS("IN_PROGRESS"),
COMPLETED("COMPLETED"),
CANCELED("CANCELED");
private String status;
TaskStatus(String status) {
this.status = status;
}
public String getStatus() {
return status;
}
}

View file

@ -2,8 +2,10 @@ package net.beetechgroup.beetask.frameworks.config;
import jakarta.enterprise.context.ApplicationScoped;
import jakarta.enterprise.inject.Produces;
import net.beetechgroup.beetask.usecase.createtask.CreateTaskUseCase;
import net.beetechgroup.beetask.usecase.repository.TaskRepository;
import net.beetechgroup.beetask.usecase.task.create.CreateTaskUseCase;
import net.beetechgroup.beetask.usecase.task.start.StartTaskUseCase;
import net.beetechgroup.beetask.usecase.task.stop.StopTaskUseCase;
@ApplicationScoped
public class TaskUseCaseConfig {
@ -12,4 +14,14 @@ public class TaskUseCaseConfig {
public CreateTaskUseCase createTaskUseCase(TaskRepository taskRepository) {
return new CreateTaskUseCase(taskRepository);
}
@Produces
public StartTaskUseCase startTaskUseCase(TaskRepository taskRepository) {
return new StartTaskUseCase(taskRepository);
}
@Produces
public StopTaskUseCase stopTaskUseCase(TaskRepository taskRepository) {
return new StopTaskUseCase(taskRepository);
}
}

View file

@ -2,11 +2,15 @@ package net.beetechgroup.beetask.frameworks.persistence.entities;
import java.util.List;
import jakarta.persistence.CollectionTable;
import jakarta.persistence.ElementCollection;
import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
import jakarta.persistence.JoinColumn;
import jakarta.persistence.Table;
import net.beetechgroup.beetask.entities.TaskStatus;
@Entity
@Table(name = "tasks")
@ -16,12 +20,19 @@ public class TaskEntity {
private Long id;
private String title;
private String description;
private String status;
private TaskStatus status;
private String project;
@jakarta.persistence.ElementCollection
@jakarta.persistence.CollectionTable(name = "task_activities", joinColumns = @jakarta.persistence.JoinColumn(name = "task_id"))
@jakarta.persistence.Column(name = "activity")
private List<String> activities;
@ElementCollection
@CollectionTable(name = "task_history", joinColumns = @JoinColumn(name = "task_id"))
private List<TaskHistoryItemEntity> history;
public void setHistory(List<TaskHistoryItemEntity> history) {
this.history = history;
}
public List<TaskHistoryItemEntity> getHistory() {
return history;
}
public Long getId() {
return id;
@ -47,11 +58,11 @@ public class TaskEntity {
this.description = description;
}
public String getStatus() {
public TaskStatus getStatus() {
return status;
}
public void setStatus(String status) {
public void setStatus(TaskStatus status) {
this.status = status;
}
@ -63,12 +74,4 @@ public class TaskEntity {
this.project = project;
}
public List<String> getActivities() {
return activities;
}
public void setActivities(List<String> activities) {
this.activities = activities;
}
}

View file

@ -0,0 +1,27 @@
package net.beetechgroup.beetask.frameworks.persistence.entities;
import java.time.LocalDateTime;
import jakarta.persistence.Embeddable;
@Embeddable
public class TaskHistoryItemEntity {
private LocalDateTime startAt;
private LocalDateTime endAt;
public LocalDateTime getStartAt() {
return startAt;
}
public void setStartAt(LocalDateTime startAt) {
this.startAt = startAt;
}
public LocalDateTime getEndAt() {
return endAt;
}
public void setEndAt(LocalDateTime endAt) {
this.endAt = endAt;
}
}

View file

@ -0,0 +1,22 @@
package net.beetechgroup.beetask.frameworks.persistence.mapper;
import net.beetechgroup.beetask.entities.TaskHistoryItem;
import net.beetechgroup.beetask.frameworks.persistence.entities.TaskHistoryItemEntity;
public class TaskHistoryItemMapper {
public static TaskHistoryItemEntity toEntity(TaskHistoryItem item) {
TaskHistoryItemEntity entity = new TaskHistoryItemEntity();
entity.setStartAt(item.getStartAt());
entity.setEndAt(item.getEndAt());
return entity;
}
public static TaskHistoryItem toDomain(TaskHistoryItemEntity entity) {
if (entity == null) return null;
TaskHistoryItem item = new TaskHistoryItem();
item.setStartAt(entity.getStartAt());
item.setEndAt(entity.getEndAt());
return item;
}
}

View file

@ -1,5 +1,6 @@
package net.beetechgroup.beetask.frameworks.persistence.mapper;
import java.util.ArrayList;
import net.beetechgroup.beetask.entities.Task;
import net.beetechgroup.beetask.frameworks.persistence.entities.TaskEntity;
@ -12,7 +13,7 @@ public class TaskMapper {
entity.setDescription(task.getDescription());
entity.setStatus(task.getStatus());
entity.setProject(task.getProject());
entity.setActivities(task.getActivities());
entity.setHistory(new ArrayList<>(task.getHistory().stream().map(TaskHistoryItemMapper::toEntity).toList()));
return entity;
}
@ -25,7 +26,7 @@ public class TaskMapper {
task.setDescription(entity.getDescription());
task.setStatus(entity.getStatus());
task.setProject(entity.getProject());
task.setActivities(entity.getActivities());
task.setHistory(new ArrayList<>(entity.getHistory().stream().map(TaskHistoryItemMapper::toDomain).toList()));
return task;
}

View file

@ -7,6 +7,7 @@ import jakarta.transaction.Transactional;
import net.beetechgroup.beetask.entities.Task;
import net.beetechgroup.beetask.frameworks.persistence.entities.TaskEntity;
import net.beetechgroup.beetask.frameworks.persistence.mapper.TaskMapper;
import net.beetechgroup.beetask.usecase.exceptions.TaskNotFoundException;
import net.beetechgroup.beetask.usecase.repository.TaskRepository;
@ApplicationScoped
@ -19,7 +20,20 @@ public class TaskRepositoryImpl implements TaskRepository {
@Transactional
public Task save(Task task) {
TaskEntity entity = TaskMapper.toEntity(task);
em.persist(entity);
if (entity.getId() == null) {
em.persist(entity);
} else {
entity = em.merge(entity);
}
return TaskMapper.toDomain(entity);
}
@Override
public Task findById(Long id) {
TaskEntity entity = em.find(TaskEntity.class, id);
if (entity == null) {
throw new TaskNotFoundException("Task not found");
}
return TaskMapper.toDomain(entity);
}
}

View file

@ -3,7 +3,9 @@ package net.beetechgroup.beetask.interfaceadapters.controllers;
import jakarta.inject.Inject;
import jakarta.ws.rs.Consumes;
import jakarta.ws.rs.POST;
import jakarta.ws.rs.PUT;
import jakarta.ws.rs.Path;
import jakarta.ws.rs.PathParam;
import jakarta.ws.rs.Produces;
import jakarta.ws.rs.core.MediaType;
import org.eclipse.microprofile.openapi.annotations.Operation;
@ -13,7 +15,9 @@ import net.beetechgroup.beetask.entities.Task;
import net.beetechgroup.beetask.interfaceadapters.dtos.TaskDTOMapper;
import net.beetechgroup.beetask.interfaceadapters.dtos.TaskRequestDTO;
import net.beetechgroup.beetask.interfaceadapters.dtos.TaskResponseDTO;
import net.beetechgroup.beetask.usecase.createtask.CreateTaskUseCase;
import net.beetechgroup.beetask.usecase.task.create.CreateTaskUseCase;
import net.beetechgroup.beetask.usecase.task.start.StartTaskUseCase;
import net.beetechgroup.beetask.usecase.task.stop.StopTaskUseCase;
@Path("/tasks")
@Produces(MediaType.APPLICATION_JSON)
@ -24,11 +28,36 @@ public class TaskController {
@Inject
CreateTaskUseCase createTaskUseCase;
@Inject
StartTaskUseCase startTaskUseCase;
@Inject
StopTaskUseCase stopTaskUseCase;
@POST
@Operation(summary = "Create a new task", description = "Creates a new task in the system")
@APIResponse(responseCode = "201", description = "Task created successfully")
public TaskResponseDTO createTask(TaskRequestDTO request) {
Task task = createTaskUseCase.execute(TaskDTOMapper.toInput(request));
return TaskDTOMapper.toResponse(task);
Task task = createTaskUseCase.execute(TaskDTOMapper.toCreateTaskInput(request));
return TaskDTOMapper.toTaskResponseDTO(task);
}
@PUT
@Path("/{id}/start")
@Operation(summary = "Start a task", description = "Starts a task in the system")
@APIResponse(responseCode = "200", description = "Task started successfully")
public TaskResponseDTO startTask(@PathParam("id") Long id) {
Task task = startTaskUseCase.execute(TaskDTOMapper.toStartTaskInput(id));
return TaskDTOMapper.toTaskResponseDTO(task);
}
@PUT
@Path("/{id}/stop")
@Operation(summary = "Stop a task", description = "Stops a task in the system")
@APIResponse(responseCode = "200", description = "Task stopped successfully")
public TaskResponseDTO stopTask(@PathParam("id") Long id) {
Task task = stopTaskUseCase.execute(TaskDTOMapper.toStopTaskInput(id));
return TaskDTOMapper.toTaskResponseDTO(task);
}
}

View file

@ -1,28 +1,37 @@
package net.beetechgroup.beetask.interfaceadapters.dtos;
import net.beetechgroup.beetask.entities.Task;
import net.beetechgroup.beetask.usecase.createtask.CreateTaskInput;
import net.beetechgroup.beetask.usecase.task.create.CreateTaskInput;
import net.beetechgroup.beetask.usecase.task.start.StartTaskInput;
import net.beetechgroup.beetask.usecase.task.stop.StopTaskInput;
public class TaskDTOMapper {
public static CreateTaskInput toInput(TaskRequestDTO request) {
public static CreateTaskInput toCreateTaskInput(TaskRequestDTO request) {
return new CreateTaskInput(
request.title(),
request.description(),
request.status(),
request.project(),
request.activities()
request.project()
);
}
public static TaskResponseDTO toResponse(Task task) {
public static TaskResponseDTO toTaskResponseDTO(Task task) {
return new TaskResponseDTO(
task.getId(),
task.getTitle(),
task.getDescription(),
task.getStatus(),
task.getProject(),
task.getActivities()
task.getHistory()
);
}
public static StartTaskInput toStartTaskInput(Long id) {
return new StartTaskInput(id);
}
public static StopTaskInput toStopTaskInput(Long id) {
return new StopTaskInput(id);
}
}

View file

@ -1,11 +1,11 @@
package net.beetechgroup.beetask.interfaceadapters.dtos;
import java.util.List;
import net.beetechgroup.beetask.entities.TaskStatus;
public record TaskRequestDTO(
Long id,
String title,
String description,
String status,
String project,
List<String> activities) {
TaskStatus status,
String project) {
}

View file

@ -2,11 +2,14 @@ package net.beetechgroup.beetask.interfaceadapters.dtos;
import java.util.List;
import net.beetechgroup.beetask.entities.TaskHistoryItem;
import net.beetechgroup.beetask.entities.TaskStatus;
public record TaskResponseDTO(
Long id,
String title,
String description,
String status,
String project,
List<String> activities) {
Long id,
String title,
String description,
TaskStatus status,
String project,
List<TaskHistoryItem> history) {
}

View file

@ -1,11 +0,0 @@
package net.beetechgroup.beetask.usecase.createtask;
import java.util.List;
public record CreateTaskInput(
String title,
String description,
String status,
String project,
List<String> activities) {
}

View file

@ -1,5 +0,0 @@
package net.beetechgroup.beetask.usecase.createtask;
public class CreateTaskOutput {
}

View file

@ -0,0 +1,7 @@
package net.beetechgroup.beetask.usecase.exceptions;
public class TaskNotFoundException extends RuntimeException {
public TaskNotFoundException(String message) {
super(message);
}
}

View file

@ -4,4 +4,5 @@ import net.beetechgroup.beetask.entities.Task;
public interface TaskRepository {
Task save(Task task);
Task findById(Long id);
}

View file

@ -0,0 +1,10 @@
package net.beetechgroup.beetask.usecase.task.create;
import net.beetechgroup.beetask.entities.TaskStatus;
public record CreateTaskInput(
String title,
String description,
TaskStatus status,
String project) {
}

View file

@ -0,0 +1,5 @@
package net.beetechgroup.beetask.usecase.task.create;
public class CreateTaskOutput {
}

View file

@ -1,4 +1,4 @@
package net.beetechgroup.beetask.usecase.createtask;
package net.beetechgroup.beetask.usecase.task.create;
import net.beetechgroup.beetask.entities.Task;
import net.beetechgroup.beetask.usecase.repository.TaskRepository;
@ -16,7 +16,6 @@ public class CreateTaskUseCase {
task.setDescription(input.description());
task.setStatus(input.status());
task.setProject(input.project());
task.setActivities(input.activities());
return taskRepository.save(task);
}
}

View file

@ -0,0 +1,4 @@
package net.beetechgroup.beetask.usecase.task.start;
public record StartTaskInput(Long id) {
}

View file

@ -0,0 +1,19 @@
package net.beetechgroup.beetask.usecase.task.start;
import net.beetechgroup.beetask.entities.Task;
import net.beetechgroup.beetask.usecase.repository.TaskRepository;
public class StartTaskUseCase {
private final TaskRepository taskRepository;
public StartTaskUseCase(TaskRepository taskRepository) {
this.taskRepository = taskRepository;
}
public Task execute(StartTaskInput input) {
Task task = taskRepository.findById(input.id());
task.start();
return taskRepository.save(task);
}
}

View file

@ -0,0 +1,4 @@
package net.beetechgroup.beetask.usecase.task.stop;
public record StopTaskInput(Long id) {
}

View file

@ -0,0 +1,19 @@
package net.beetechgroup.beetask.usecase.task.stop;
import net.beetechgroup.beetask.entities.Task;
import net.beetechgroup.beetask.usecase.repository.TaskRepository;
public class StopTaskUseCase {
private final TaskRepository taskRepository;
public StopTaskUseCase(TaskRepository taskRepository) {
this.taskRepository = taskRepository;
}
public Task execute(StopTaskInput input) {
Task task = taskRepository.findById(input.id());
task.stop();
return taskRepository.save(task);
}
}