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; package net.beetechgroup.beetask.entities;
import java.time.Duration;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List; import java.util.List;
public class Task { public class Task {
private Long id; private Long id;
private String title; private String title;
private String description; private String description;
private String status; private TaskStatus status;
private String project; private String project;
private List<String> activities; private List<TaskHistoryItem> history = new ArrayList<>();
public Long getId() { public Long getId() {
return id; return id;
@ -34,11 +37,11 @@ public class Task {
this.description = description; this.description = description;
} }
public String getStatus() { public TaskStatus getStatus() {
return status; return status;
} }
public void setStatus(String status) { public void setStatus(TaskStatus status) {
this.status = status; this.status = status;
} }
@ -50,12 +53,52 @@ public class Task {
this.project = project; this.project = project;
} }
public List<String> getActivities() { public List<TaskHistoryItem> getHistory() {
return activities; return history;
} }
public void setActivities(List<String> activities) { public void setHistory(List<TaskHistoryItem> history) {
this.activities = activities; 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.context.ApplicationScoped;
import jakarta.enterprise.inject.Produces; import jakarta.enterprise.inject.Produces;
import net.beetechgroup.beetask.usecase.createtask.CreateTaskUseCase;
import net.beetechgroup.beetask.usecase.repository.TaskRepository; 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 @ApplicationScoped
public class TaskUseCaseConfig { public class TaskUseCaseConfig {
@ -12,4 +14,14 @@ public class TaskUseCaseConfig {
public CreateTaskUseCase createTaskUseCase(TaskRepository taskRepository) { public CreateTaskUseCase createTaskUseCase(TaskRepository taskRepository) {
return new CreateTaskUseCase(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 java.util.List;
import jakarta.persistence.CollectionTable;
import jakarta.persistence.ElementCollection;
import jakarta.persistence.Entity; import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue; import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType; import jakarta.persistence.GenerationType;
import jakarta.persistence.Id; import jakarta.persistence.Id;
import jakarta.persistence.JoinColumn;
import jakarta.persistence.Table; import jakarta.persistence.Table;
import net.beetechgroup.beetask.entities.TaskStatus;
@Entity @Entity
@Table(name = "tasks") @Table(name = "tasks")
@ -16,12 +20,19 @@ public class TaskEntity {
private Long id; private Long id;
private String title; private String title;
private String description; private String description;
private String status; private TaskStatus status;
private String project; private String project;
@jakarta.persistence.ElementCollection @ElementCollection
@jakarta.persistence.CollectionTable(name = "task_activities", joinColumns = @jakarta.persistence.JoinColumn(name = "task_id")) @CollectionTable(name = "task_history", joinColumns = @JoinColumn(name = "task_id"))
@jakarta.persistence.Column(name = "activity") private List<TaskHistoryItemEntity> history;
private List<String> activities;
public void setHistory(List<TaskHistoryItemEntity> history) {
this.history = history;
}
public List<TaskHistoryItemEntity> getHistory() {
return history;
}
public Long getId() { public Long getId() {
return id; return id;
@ -47,11 +58,11 @@ public class TaskEntity {
this.description = description; this.description = description;
} }
public String getStatus() { public TaskStatus getStatus() {
return status; return status;
} }
public void setStatus(String status) { public void setStatus(TaskStatus status) {
this.status = status; this.status = status;
} }
@ -63,12 +74,4 @@ public class TaskEntity {
this.project = project; 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; package net.beetechgroup.beetask.frameworks.persistence.mapper;
import java.util.ArrayList;
import net.beetechgroup.beetask.entities.Task; import net.beetechgroup.beetask.entities.Task;
import net.beetechgroup.beetask.frameworks.persistence.entities.TaskEntity; import net.beetechgroup.beetask.frameworks.persistence.entities.TaskEntity;
@ -12,7 +13,7 @@ public class TaskMapper {
entity.setDescription(task.getDescription()); entity.setDescription(task.getDescription());
entity.setStatus(task.getStatus()); entity.setStatus(task.getStatus());
entity.setProject(task.getProject()); entity.setProject(task.getProject());
entity.setActivities(task.getActivities()); entity.setHistory(new ArrayList<>(task.getHistory().stream().map(TaskHistoryItemMapper::toEntity).toList()));
return entity; return entity;
} }
@ -25,7 +26,7 @@ public class TaskMapper {
task.setDescription(entity.getDescription()); task.setDescription(entity.getDescription());
task.setStatus(entity.getStatus()); task.setStatus(entity.getStatus());
task.setProject(entity.getProject()); task.setProject(entity.getProject());
task.setActivities(entity.getActivities()); task.setHistory(new ArrayList<>(entity.getHistory().stream().map(TaskHistoryItemMapper::toDomain).toList()));
return task; return task;
} }

View file

@ -7,6 +7,7 @@ import jakarta.transaction.Transactional;
import net.beetechgroup.beetask.entities.Task; import net.beetechgroup.beetask.entities.Task;
import net.beetechgroup.beetask.frameworks.persistence.entities.TaskEntity; import net.beetechgroup.beetask.frameworks.persistence.entities.TaskEntity;
import net.beetechgroup.beetask.frameworks.persistence.mapper.TaskMapper; import net.beetechgroup.beetask.frameworks.persistence.mapper.TaskMapper;
import net.beetechgroup.beetask.usecase.exceptions.TaskNotFoundException;
import net.beetechgroup.beetask.usecase.repository.TaskRepository; import net.beetechgroup.beetask.usecase.repository.TaskRepository;
@ApplicationScoped @ApplicationScoped
@ -19,7 +20,20 @@ public class TaskRepositoryImpl implements TaskRepository {
@Transactional @Transactional
public Task save(Task task) { public Task save(Task task) {
TaskEntity entity = TaskMapper.toEntity(task); TaskEntity entity = TaskMapper.toEntity(task);
if (entity.getId() == null) {
em.persist(entity); 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); return TaskMapper.toDomain(entity);
} }
} }

View file

@ -3,7 +3,9 @@ package net.beetechgroup.beetask.interfaceadapters.controllers;
import jakarta.inject.Inject; import jakarta.inject.Inject;
import jakarta.ws.rs.Consumes; import jakarta.ws.rs.Consumes;
import jakarta.ws.rs.POST; import jakarta.ws.rs.POST;
import jakarta.ws.rs.PUT;
import jakarta.ws.rs.Path; import jakarta.ws.rs.Path;
import jakarta.ws.rs.PathParam;
import jakarta.ws.rs.Produces; import jakarta.ws.rs.Produces;
import jakarta.ws.rs.core.MediaType; import jakarta.ws.rs.core.MediaType;
import org.eclipse.microprofile.openapi.annotations.Operation; 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.TaskDTOMapper;
import net.beetechgroup.beetask.interfaceadapters.dtos.TaskRequestDTO; import net.beetechgroup.beetask.interfaceadapters.dtos.TaskRequestDTO;
import net.beetechgroup.beetask.interfaceadapters.dtos.TaskResponseDTO; 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") @Path("/tasks")
@Produces(MediaType.APPLICATION_JSON) @Produces(MediaType.APPLICATION_JSON)
@ -24,11 +28,36 @@ public class TaskController {
@Inject @Inject
CreateTaskUseCase createTaskUseCase; CreateTaskUseCase createTaskUseCase;
@Inject
StartTaskUseCase startTaskUseCase;
@Inject
StopTaskUseCase stopTaskUseCase;
@POST @POST
@Operation(summary = "Create a new task", description = "Creates a new task in the system") @Operation(summary = "Create a new task", description = "Creates a new task in the system")
@APIResponse(responseCode = "201", description = "Task created successfully") @APIResponse(responseCode = "201", description = "Task created successfully")
public TaskResponseDTO createTask(TaskRequestDTO request) { public TaskResponseDTO createTask(TaskRequestDTO request) {
Task task = createTaskUseCase.execute(TaskDTOMapper.toInput(request)); Task task = createTaskUseCase.execute(TaskDTOMapper.toCreateTaskInput(request));
return TaskDTOMapper.toResponse(task); 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; package net.beetechgroup.beetask.interfaceadapters.dtos;
import net.beetechgroup.beetask.entities.Task; 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 class TaskDTOMapper {
public static CreateTaskInput toInput(TaskRequestDTO request) { public static CreateTaskInput toCreateTaskInput(TaskRequestDTO request) {
return new CreateTaskInput( return new CreateTaskInput(
request.title(), request.title(),
request.description(), request.description(),
request.status(), request.status(),
request.project(), request.project()
request.activities()
); );
} }
public static TaskResponseDTO toResponse(Task task) { public static TaskResponseDTO toTaskResponseDTO(Task task) {
return new TaskResponseDTO( return new TaskResponseDTO(
task.getId(), task.getId(),
task.getTitle(), task.getTitle(),
task.getDescription(), task.getDescription(),
task.getStatus(), task.getStatus(),
task.getProject(), 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; package net.beetechgroup.beetask.interfaceadapters.dtos;
import java.util.List; import net.beetechgroup.beetask.entities.TaskStatus;
public record TaskRequestDTO( public record TaskRequestDTO(
Long id,
String title, String title,
String description, String description,
String status, TaskStatus status,
String project, String project) {
List<String> activities) {
} }

View file

@ -2,11 +2,14 @@ package net.beetechgroup.beetask.interfaceadapters.dtos;
import java.util.List; import java.util.List;
import net.beetechgroup.beetask.entities.TaskHistoryItem;
import net.beetechgroup.beetask.entities.TaskStatus;
public record TaskResponseDTO( public record TaskResponseDTO(
Long id, Long id,
String title, String title,
String description, String description,
String status, TaskStatus status,
String project, String project,
List<String> activities) { 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 { public interface TaskRepository {
Task save(Task task); 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.entities.Task;
import net.beetechgroup.beetask.usecase.repository.TaskRepository; import net.beetechgroup.beetask.usecase.repository.TaskRepository;
@ -16,7 +16,6 @@ public class CreateTaskUseCase {
task.setDescription(input.description()); task.setDescription(input.description());
task.setStatus(input.status()); task.setStatus(input.status());
task.setProject(input.project()); task.setProject(input.project());
task.setActivities(input.activities());
return taskRepository.save(task); 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);
}
}