diff --git a/src/main/java/clap/server/adapter/outbound/persistense/TaskPersistenceAdapter.java b/src/main/java/clap/server/adapter/outbound/persistense/TaskPersistenceAdapter.java index 57fefeaf..ca989a80 100644 --- a/src/main/java/clap/server/adapter/outbound/persistense/TaskPersistenceAdapter.java +++ b/src/main/java/clap/server/adapter/outbound/persistense/TaskPersistenceAdapter.java @@ -81,14 +81,26 @@ public Page findAllTasks(Pageable pageable, FilterTaskListRequest filterTa } @Override - public Optional findPrevOrderTaskByProcessorIdAndStatus(Long processorId, TaskStatus taskStatus, Long processorOrder) { - Optional taskEntity = taskRepository.findTopByProcessor_MemberIdAndTaskStatusAndProcessorOrderLessThanOrderByProcessorOrderDesc(processorId, taskStatus, processorOrder); + public Optional findPrevOrderTaskByProcessorOrderAndStatus(Long processorId, TaskStatus taskStatus, Long processorOrder) { + Optional taskEntity = taskRepository.findTopByProcessor_MemberIdAndTaskStatusAndProcessorOrderLessThanOrderByProcessorOrderAsc(processorId, taskStatus, processorOrder); return taskEntity.map(taskPersistenceMapper::toDomain); } @Override - public Optional findNextOrderTaskByProcessorIdAndStatus(Long processorId, TaskStatus taskStatus, Long processorOrder) { - Optional taskEntity = taskRepository.findTopByProcessor_MemberIdAndTaskStatusAndProcessorOrderAfterOrderByProcessorOrderDesc(processorId, taskStatus, processorOrder); + public Optional findNextOrderTaskByProcessorOrderAndStatus(Long processorId, TaskStatus taskStatus, Long processorOrder) { + Optional taskEntity = taskRepository.findTopByProcessor_MemberIdAndTaskStatusAndProcessorOrderAfterOrderByProcessorOrderAsc(processorId, taskStatus, processorOrder); + return taskEntity.map(taskPersistenceMapper::toDomain); + } + + @Override + public Optional findPrevOrderTaskByTaskIdAndStatus(Long processorId, TaskStatus taskStatus, Long taskId) { + Optional taskEntity = taskRepository.findTopByProcessor_MemberIdAndTaskStatusAndTaskIdLessThanOrderByTaskIdDesc(processorId, taskStatus, taskId); + return taskEntity.map(taskPersistenceMapper::toDomain); + } + + @Override + public Optional findNextOrderTaskByTaskIdAndStatus(Long processorId, TaskStatus taskStatus, Long taskId) { + Optional taskEntity = taskRepository.findTopByProcessor_MemberIdAndTaskStatusAndTaskIdGreaterThanOrderByTaskIdAsc(processorId, taskStatus, taskId); return taskEntity.map(taskPersistenceMapper::toDomain); } diff --git a/src/main/java/clap/server/adapter/outbound/persistense/repository/task/TaskRepository.java b/src/main/java/clap/server/adapter/outbound/persistense/repository/task/TaskRepository.java index 5c8fd21e..fe78f507 100644 --- a/src/main/java/clap/server/adapter/outbound/persistense/repository/task/TaskRepository.java +++ b/src/main/java/clap/server/adapter/outbound/persistense/repository/task/TaskRepository.java @@ -24,7 +24,6 @@ public interface TaskRepository extends JpaRepository, TaskCus @Query("select t from TaskEntity t join fetch t.processor p" + " where t.updatedAt between :updatedAtAfter and :updatedAtBefore") - List findYesterdayTaskByUpdatedAtIsBetween( @Param("updatedAtAfter") LocalDateTime updatedAtAfter, @Param("updatedAtBefore") LocalDateTime updatedAtBefore @@ -49,14 +48,17 @@ List findTasksWithTaskStatusAndCompletedAt( Optional findByTaskIdAndTaskStatus(Long id, TaskStatus status); - Optional findTopByProcessor_MemberIdAndTaskStatusAndProcessorOrderLessThanOrderByProcessorOrderDesc(Long processorId, TaskStatus taskStatus, Long processorOrder); + Optional findTopByProcessor_MemberIdAndTaskStatusAndProcessorOrderLessThanOrderByProcessorOrderAsc(Long processorId, TaskStatus taskStatus, Long processorOrder); - Optional findTopByProcessor_MemberIdAndTaskStatusAndProcessorOrderAfterOrderByProcessorOrderDesc( + Optional findTopByProcessor_MemberIdAndTaskStatusAndProcessorOrderAfterOrderByProcessorOrderAsc( Long processorId, TaskStatus taskStatus, Long processorOrder); @Query("SELECT t FROM TaskEntity t JOIN FETCH t.processor p WHERE (:memberId IS NULL OR p.memberId = :memberId) ") Page findTeamStatus(@Param("memberId") Long memberId, FilterTeamStatusRequest filter, Pageable pageable); + Optional findTopByProcessor_MemberIdAndTaskStatusAndTaskIdLessThanOrderByTaskIdDesc(Long processorId, TaskStatus taskStatus, Long taskId); + + Optional findTopByProcessor_MemberIdAndTaskStatusAndTaskIdGreaterThanOrderByTaskIdAsc(Long processorId, TaskStatus status, Long taskId); } \ No newline at end of file diff --git a/src/main/java/clap/server/application/port/outbound/task/LoadTaskPort.java b/src/main/java/clap/server/application/port/outbound/task/LoadTaskPort.java index a069ab78..0b385460 100644 --- a/src/main/java/clap/server/application/port/outbound/task/LoadTaskPort.java +++ b/src/main/java/clap/server/application/port/outbound/task/LoadTaskPort.java @@ -28,9 +28,13 @@ public interface LoadTaskPort { Optional findByIdAndStatus(Long id, TaskStatus status); - Optional findPrevOrderTaskByProcessorIdAndStatus(Long processorId, TaskStatus taskStatus, Long processorOrder); + Optional findPrevOrderTaskByProcessorOrderAndStatus(Long processorId, TaskStatus taskStatus, Long processorOrder); - Optional findNextOrderTaskByProcessorIdAndStatus(Long processorId, TaskStatus taskStatus, Long processorOrder); + Optional findNextOrderTaskByProcessorOrderAndStatus(Long processorId, TaskStatus taskStatus, Long processorOrder); + + Optional findPrevOrderTaskByTaskIdAndStatus(Long processorId, TaskStatus taskStatus, Long taskId); + + Optional findNextOrderTaskByTaskIdAndStatus(Long processorId, TaskStatus taskStatus, Long taskId); List findTaskBoardByFilter(Long processorId, List statuses, LocalDateTime untilDateTime, FilterTaskBoardRequest request); diff --git a/src/main/java/clap/server/application/service/task/UpdateTaskBoardService.java b/src/main/java/clap/server/application/service/task/UpdateTaskBoardService.java index e85a1831..75d4f099 100644 --- a/src/main/java/clap/server/application/service/task/UpdateTaskBoardService.java +++ b/src/main/java/clap/server/application/service/task/UpdateTaskBoardService.java @@ -1,12 +1,14 @@ package clap.server.application.service.task; import clap.server.adapter.inbound.web.dto.task.request.UpdateTaskOrderRequest; +import clap.server.adapter.outbound.persistense.entity.notification.constant.NotificationType; import clap.server.adapter.outbound.persistense.entity.task.constant.TaskStatus; import clap.server.application.port.inbound.domain.MemberService; import clap.server.application.port.inbound.domain.TaskService; import clap.server.application.port.inbound.task.UpdateTaskBoardUsecase; import clap.server.application.port.inbound.task.UpdateTaskOrderAndStatusUsecase; import clap.server.application.port.outbound.task.LoadTaskPort; +import clap.server.application.service.webhook.SendNotificationService; import clap.server.common.annotation.architecture.ApplicationService; import clap.server.domain.model.member.Member; import clap.server.domain.model.task.Task; @@ -19,6 +21,8 @@ import lombok.extern.slf4j.Slf4j; import org.springframework.transaction.annotation.Transactional; +import java.util.List; + @Slf4j @ApplicationService @RequiredArgsConstructor @@ -26,6 +30,7 @@ class UpdateTaskBoardService implements UpdateTaskBoardUsecase, UpdateTaskOrderA private final MemberService memberService; private final TaskService taskService; private final LoadTaskPort loadTaskPort; + private final SendNotificationService sendNotificationService; private final TaskOrderCalculationPolicy taskOrderCalculationPolicy; private final ProcessorValidationPolicy processorValidationPolicy; @@ -53,7 +58,7 @@ public void updateTaskOrder(Long processorId, UpdateTaskOrderRequest request) { if (request.prevTaskId() == 0) { Task nextTask = findByIdAndStatus(request.nextTaskId(), targetTask.getTaskStatus()); // 해당 상태에서 바로 앞에 있는 작업 찾기 - Task prevTask = loadTaskPort.findPrevOrderTaskByProcessorIdAndStatus(processorId, targetTask.getTaskStatus(), nextTask.getProcessorOrder()).orElse(null); + Task prevTask = loadTaskPort.findPrevOrderTaskByProcessorOrderAndStatus(processorId, targetTask.getTaskStatus(), nextTask.getProcessorOrder()).orElse(null); long newOrder = taskOrderCalculationPolicy.calculateOrderForTop(prevTask, nextTask); updateNewTaskOrder(targetTask, newOrder); } @@ -61,7 +66,7 @@ public void updateTaskOrder(Long processorId, UpdateTaskOrderRequest request) { else if (request.nextTaskId() == 0) { Task prevTask = findByIdAndStatus(request.prevTaskId(), targetTask.getTaskStatus()); // 해당 상태에서 바로 뒤에 있는 작업 찾기 - Task nextTask = loadTaskPort.findNextOrderTaskByProcessorIdAndStatus(processorId, targetTask.getTaskStatus(), prevTask.getProcessorOrder()).orElse(null); + Task nextTask = loadTaskPort.findNextOrderTaskByProcessorOrderAndStatus(processorId, targetTask.getTaskStatus(), prevTask.getProcessorOrder()).orElse(null); long newOrder = taskOrderCalculationPolicy.calculateOrderForBottom(prevTask, nextTask); updateNewTaskOrder(targetTask, newOrder); } else { @@ -98,49 +103,87 @@ public void updateTaskOrderAndStatus(Long processorId, UpdateTaskOrderRequest re Task targetTask = taskService.findById(request.targetTaskId()); processorValidationPolicy.validateProcessor(processorId, targetTask); - if (request.prevTaskId() == 0) { - Task nextTask = findByIdAndStatus(request.nextTaskId(), targetStatus); + Task updatedTask; + Task prevTask; + Task nextTask; + + // 조회된 작업 보드에서 하나의 작업만 존재하고, 이 작업을 이동할 때 + if (request.prevTaskId() == 0 && request.nextTaskId() == 0) { + + // 요청 시간 기준으로 가장 가장 근접한 이전의 Task를 조회 + prevTask = loadTaskPort.findPrevOrderTaskByTaskIdAndStatus(processorId, targetStatus, targetTask.getTaskId()).orElse(null); + if (prevTask != null) { + // 이전 Task가 있다면 바로 다음의 Task 조회 + nextTask = loadTaskPort.findNextOrderTaskByProcessorOrderAndStatus(processorId, targetStatus, prevTask.getProcessorOrder()).orElse(null); + } // 요청 시간 기준으로 가장 가장 근접한 이후의 Task를 조회 + else + nextTask = loadTaskPort.findNextOrderTaskByTaskIdAndStatus(processorId, targetStatus, targetTask.getTaskId()).orElse(null); + + // 하나의 task만 존재할 경우 상태만 update + if (prevTask == null && nextTask == null) { + targetTask.updateTaskStatus(targetStatus); + updatedTask = taskService.upsert(targetTask); + } else if (prevTask == null) { + long newOrder = taskOrderCalculationPolicy.calculateOrderForBottom(null, nextTask); + updatedTask = updateNewTaskOrderAndStatus(targetStatus, targetTask, newOrder); + } else if (nextTask == null) { + long newOrder = taskOrderCalculationPolicy.calculateOrderForBottom(prevTask, null); + updatedTask = updateNewTaskOrderAndStatus(targetStatus, targetTask, newOrder); + } else { + long newOrder = taskOrderCalculationPolicy.calculateNewProcessorOrder(prevTask.getProcessorOrder(), nextTask.getProcessorOrder()); + updatedTask = updateNewTaskOrderAndStatus(targetStatus, targetTask, newOrder); + } + } else if (request.prevTaskId() == 0) { + nextTask = findByIdAndStatus(request.nextTaskId(), targetStatus); // 해당 상태에서 바로 앞 있는 작업 찾기 - Task prevTask = loadTaskPort.findPrevOrderTaskByProcessorIdAndStatus(processorId, targetStatus, nextTask.getProcessorOrder()).orElse(null); + prevTask = loadTaskPort.findPrevOrderTaskByProcessorOrderAndStatus(processorId, targetStatus, nextTask.getProcessorOrder()).orElse(null); long newOrder = taskOrderCalculationPolicy.calculateOrderForTop(prevTask, nextTask); - updateNewTaskOrderAndStatus(targetStatus, targetTask, newOrder); + updatedTask = updateNewTaskOrderAndStatus(targetStatus, targetTask, newOrder); } else if (request.nextTaskId() == 0) { - Task prevTask = findByIdAndStatus(request.prevTaskId(), targetStatus); + prevTask = findByIdAndStatus(request.prevTaskId(), targetStatus); // 해당 상태에서 바로 뒤에 있는 작업 찾기 - Task nextTask = loadTaskPort.findNextOrderTaskByProcessorIdAndStatus(processorId, targetStatus, prevTask.getProcessorOrder()).orElse(null); + nextTask = loadTaskPort.findNextOrderTaskByProcessorOrderAndStatus(processorId, targetStatus, prevTask.getProcessorOrder()).orElse(null); long newOrder = taskOrderCalculationPolicy.calculateOrderForBottom(prevTask, nextTask); - updateNewTaskOrderAndStatus(targetStatus, targetTask, newOrder); + updatedTask = updateNewTaskOrderAndStatus(targetStatus, targetTask, newOrder); } else { - Task prevTask = findByIdAndStatus(request.prevTaskId(), targetStatus); - Task nextTask = findByIdAndStatus(request.nextTaskId(), targetStatus); + prevTask = findByIdAndStatus(request.prevTaskId(), targetStatus); + nextTask = findByIdAndStatus(request.nextTaskId(), targetStatus); long newOrder = taskOrderCalculationPolicy.calculateNewProcessorOrder(prevTask.getProcessorOrder(), nextTask.getProcessorOrder()); - updateNewTaskOrderAndStatus(targetStatus, targetTask, newOrder); + updatedTask = updateNewTaskOrderAndStatus(targetStatus, targetTask, newOrder); } + + //TODO: 최종 단계에서 주석 처리 해제 + //publishNotification(targetTask, NotificationType.STATUS_SWITCHED, String.valueOf(updatedTask.getTaskStatus())); } /** * 작업의 상태와 순서를 업데이트하는 메서드 */ - private void updateNewTaskOrderAndStatus(TaskStatus targetStatus, Task targetTask, long newOrder) { + private Task updateNewTaskOrderAndStatus(TaskStatus targetStatus, Task targetTask, long newOrder) { targetTask.updateProcessorOrder(newOrder); targetTask.updateTaskStatus(targetStatus); - taskService.upsert(targetTask); + return taskService.upsert(targetTask); } /** * 순서 변경 요청의 유효성을 검증하는 메서드 */ public void validateRequest(UpdateTaskOrderRequest request, TaskStatus targetStatus) { - // 이전 및 다음 작업 ID가 모두 0인 경우 예외 발생 - if (request.prevTaskId() == 0 && request.nextTaskId() == 0) { - throw new ApplicationException(TaskErrorCode.INVALID_TASK_STATUS_TRANSITION); - } - // 타겟 상태가 유효한지 검증 if (targetStatus != null && !TaskPolicyConstants.TASK_BOARD_STATUS_FILTER.contains(targetStatus)) { throw new ApplicationException(TaskErrorCode.INVALID_TASK_STATUS_TRANSITION); } } + private void publishNotification(Task task, NotificationType notificationType, String message) { + List receivers = List.of(task.getRequester(), task.getProcessor()); + receivers.forEach(receiver -> { + sendNotificationService.sendPushNotification(receiver, notificationType, + task, message, null); + }); + sendNotificationService.sendAgitNotification(notificationType, + task, message, null); + } + }