diff --git a/src/main/java/clap/server/adapter/inbound/web/dto/common/SliceResponse.java b/src/main/java/clap/server/adapter/inbound/web/dto/common/SliceResponse.java index 3cffcec0..d8c08509 100644 --- a/src/main/java/clap/server/adapter/inbound/web/dto/common/SliceResponse.java +++ b/src/main/java/clap/server/adapter/inbound/web/dto/common/SliceResponse.java @@ -5,8 +5,7 @@ public record SliceResponse ( List content, - int currentPage, - int size, + boolean hasNext, boolean isFirst, boolean isLast ) { diff --git a/src/main/java/clap/server/adapter/inbound/web/dto/task/response/TaskBoardResponse.java b/src/main/java/clap/server/adapter/inbound/web/dto/task/response/TaskBoardResponse.java new file mode 100644 index 00000000..1c973647 --- /dev/null +++ b/src/main/java/clap/server/adapter/inbound/web/dto/task/response/TaskBoardResponse.java @@ -0,0 +1,13 @@ +package clap.server.adapter.inbound.web.dto.task.response; + +import java.util.List; + +public record TaskBoardResponse( + List tasksInProgress, + List tasksPendingComplete, + List tasksCompleted, + boolean hasNext, + boolean isFirst, + boolean isLast +){ +} diff --git a/src/main/java/clap/server/adapter/inbound/web/dto/task/response/TaskItemResponse.java b/src/main/java/clap/server/adapter/inbound/web/dto/task/response/TaskItemResponse.java new file mode 100644 index 00000000..8fa05701 --- /dev/null +++ b/src/main/java/clap/server/adapter/inbound/web/dto/task/response/TaskItemResponse.java @@ -0,0 +1,19 @@ +package clap.server.adapter.inbound.web.dto.task.response; + +import clap.server.adapter.outbound.persistense.entity.task.constant.TaskStatus; + +import java.time.LocalDateTime; + +public record TaskItemResponse( + Long taskId, + String taskCode, + String mainCategoryName, + String categoryName, + String requesterNickname, + String requesterImageUrl, + String requesterDepartment, + long processorOrder, + TaskStatus taskStatus, + LocalDateTime createdAt +){ +} diff --git a/src/main/java/clap/server/adapter/inbound/web/task/FindTaskController.java b/src/main/java/clap/server/adapter/inbound/web/task/FindTaskController.java index e7618937..64a715e8 100644 --- a/src/main/java/clap/server/adapter/inbound/web/task/FindTaskController.java +++ b/src/main/java/clap/server/adapter/inbound/web/task/FindTaskController.java @@ -37,6 +37,7 @@ public ResponseEntity> findTasksRequestedByUser( Pageable pageable = PageRequest.of(page, pageSize); return ResponseEntity.ok(taskListUsecase.findTasksRequestedByUser(userInfo.getUserId(), pageable, filterTaskListRequest)); } + @Operation(summary = "요청한 작업 상세 조회") @Secured({"ROLE_USER", "ROLE_MANAGER"}) @GetMapping("/requests/details/{taskId}") @@ -45,7 +46,8 @@ public ResponseEntity findRequestedTaskDetails( @AuthenticationPrincipal SecurityUserDetails userInfo){ return ResponseEntity.ok(taskDetailsUsecase.findRequestedTaskDetails(userInfo.getUserId(), taskId)); } - @Operation(summary = "승인대기 중인 요청 목록 조회") + + @Operation(summary = "승인 대기 중인 요청 목록 조회") @Secured({"ROLE_MANAGER"}) @GetMapping("/requests/pending") public ResponseEntity> findPendingApprovalTasks( diff --git a/src/main/java/clap/server/adapter/inbound/web/task/TaskBoardController.java b/src/main/java/clap/server/adapter/inbound/web/task/TaskBoardController.java new file mode 100644 index 00000000..e8d62005 --- /dev/null +++ b/src/main/java/clap/server/adapter/inbound/web/task/TaskBoardController.java @@ -0,0 +1,41 @@ +package clap.server.adapter.inbound.web.task; + +import clap.server.adapter.inbound.security.SecurityUserDetails; +import clap.server.adapter.inbound.web.dto.task.response.TaskBoardResponse; +import clap.server.application.port.inbound.task.TaskBoardUsecase; +import clap.server.common.annotation.architecture.WebAdapter; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.tags.Tag; +import lombok.RequiredArgsConstructor; +import org.springframework.data.domain.PageRequest; +import org.springframework.data.domain.Pageable; +import org.springframework.format.annotation.DateTimeFormat; +import org.springframework.http.ResponseEntity; +import org.springframework.security.core.annotation.AuthenticationPrincipal; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; + +import java.time.LocalDate; + +@Tag(name = "작업 보드 조회 및 순서 변경") +@WebAdapter +@RestController +@RequiredArgsConstructor +@RequestMapping("/api/task-board") +public class TaskBoardController { + private final TaskBoardUsecase taskBoardUsecase; + + @GetMapping + public ResponseEntity getTaskBoard(@RequestParam(defaultValue = "0") int page, + @RequestParam(defaultValue = "20") int pageSize, + @Parameter(description = "yyyy-mm-dd 형식으로 입력합니다.") @RequestParam(required = false) + @DateTimeFormat(pattern = "yyyy-MM-dd") LocalDate untilDate, + @AuthenticationPrincipal SecurityUserDetails userInfo) { + Pageable pageable = PageRequest.of(page, pageSize); + return ResponseEntity.ok(taskBoardUsecase.getTaskBoards(userInfo.getUserId(), untilDate, pageable)); + } + + +} diff --git a/src/main/java/clap/server/adapter/outbound/persistense/AttachmentPersistenceAdapter.java b/src/main/java/clap/server/adapter/outbound/persistense/AttachmentPersistenceAdapter.java index 6fb71188..4efd3063 100644 --- a/src/main/java/clap/server/adapter/outbound/persistense/AttachmentPersistenceAdapter.java +++ b/src/main/java/clap/server/adapter/outbound/persistense/AttachmentPersistenceAdapter.java @@ -39,21 +39,16 @@ public void saveAll(List attachments) { @Override public List findAllByTaskIdAndCommentIsNull(final Long taskId) { - List attachmentEntities = attachmentRepository.findAllByTask_TaskIdAndCommentIsNull(taskId); + List attachmentEntities = attachmentRepository.findAllByTask_TaskIdAndCommentIsNullAndIsDeletedIsFalse(taskId); return attachmentEntities.stream() .map(attachmentPersistenceMapper::toDomain) .collect(Collectors.toList()); } public List findAllByTaskIdAndCommentIsNullAndAttachmentId(final Long taskId, final List attachmentIds) { - List attachmentEntities = attachmentRepository.findAllByTask_TaskIdAndCommentIsNullAndAttachmentIdIn(taskId, attachmentIds); + List attachmentEntities = attachmentRepository.findAllByTask_TaskIdAndCommentIsNullAndIsDeletedIsFalseAndAttachmentIdIn(taskId, attachmentIds); return attachmentEntities.stream() .map(attachmentPersistenceMapper::toDomain) .collect(Collectors.toList()); } - - @Override - public void deleteByIds(List attachmentIds) { - attachmentRepository.deleteAllByAttachmentIdIn(attachmentIds); - } } diff --git a/src/main/java/clap/server/adapter/outbound/persistense/repository/LabelPersistenceAdapter.java b/src/main/java/clap/server/adapter/outbound/persistense/LabelPersistenceAdapter.java similarity index 87% rename from src/main/java/clap/server/adapter/outbound/persistense/repository/LabelPersistenceAdapter.java rename to src/main/java/clap/server/adapter/outbound/persistense/LabelPersistenceAdapter.java index c64863fb..dd09e12a 100644 --- a/src/main/java/clap/server/adapter/outbound/persistense/repository/LabelPersistenceAdapter.java +++ b/src/main/java/clap/server/adapter/outbound/persistense/LabelPersistenceAdapter.java @@ -1,7 +1,6 @@ -package clap.server.adapter.outbound.persistense.repository; +package clap.server.adapter.outbound.persistense; import clap.server.adapter.outbound.persistense.entity.task.LabelEntity; -import clap.server.adapter.outbound.persistense.entity.task.TaskEntity; import clap.server.adapter.outbound.persistense.mapper.LabelPersistenceMapper; import clap.server.adapter.outbound.persistense.repository.task.LabelRepository; import clap.server.application.port.outbound.task.LoadLabelPort; 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 10167342..ead46326 100644 --- a/src/main/java/clap/server/adapter/outbound/persistense/TaskPersistenceAdapter.java +++ b/src/main/java/clap/server/adapter/outbound/persistense/TaskPersistenceAdapter.java @@ -4,6 +4,7 @@ import clap.server.adapter.inbound.web.dto.task.FilterTaskListRequest; import clap.server.adapter.inbound.web.dto.task.FilterTaskListResponse; import clap.server.adapter.outbound.persistense.entity.task.TaskEntity; +import clap.server.adapter.outbound.persistense.entity.task.constant.TaskStatus; import clap.server.adapter.outbound.persistense.mapper.TaskPersistenceMapper; import clap.server.adapter.outbound.persistense.repository.task.TaskRepository; import clap.server.application.mapper.TaskMapper; @@ -12,19 +13,22 @@ import clap.server.common.annotation.architecture.PersistenceAdapter; import clap.server.domain.model.task.Task; import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; +import org.springframework.data.domain.Slice; import java.time.LocalDateTime; import java.util.List; import java.util.Optional; - +@Slf4j @PersistenceAdapter @RequiredArgsConstructor public class TaskPersistenceAdapter implements CommandTaskPort , LoadTaskPort { private final TaskRepository taskRepository; private final TaskPersistenceMapper taskPersistenceMapper; + @Override public Task save(Task task) { TaskEntity taskEntity = taskPersistenceMapper.toEntity(task); @@ -52,6 +56,13 @@ public Page findPendingApprovalTasks(Pageable pag return taskList.map(TaskMapper::toFilterPendingApprovalTasksResponse); } + @Override + public Slice findByProcessorAndStatus(Long processorId, List statuses, LocalDateTime untilDate, Pageable pageable) { + log.info("untildate {}", untilDate); + Slice tasks = taskRepository.findTasksWithTaskStatusAndCompletedAt(processorId, statuses, untilDate, pageable); + return tasks.map(taskPersistenceMapper::toDomain); + } + @Override public List findYesterdayTaskByDate(LocalDateTime now) { return taskRepository.findYesterdayTaskByUpdatedAtIsBetween(now.minusDays(1), now) diff --git a/src/main/java/clap/server/adapter/outbound/persistense/entity/task/AttachmentEntity.java b/src/main/java/clap/server/adapter/outbound/persistense/entity/task/AttachmentEntity.java index 4fa02685..07336a1c 100644 --- a/src/main/java/clap/server/adapter/outbound/persistense/entity/task/AttachmentEntity.java +++ b/src/main/java/clap/server/adapter/outbound/persistense/entity/task/AttachmentEntity.java @@ -34,4 +34,7 @@ public class AttachmentEntity extends BaseTimeEntity { @OneToOne(fetch = FetchType.LAZY) @JoinColumn(name = "comment_id") private CommentEntity comment; + + @Column(nullable = false) + private boolean isDeleted; } diff --git a/src/main/java/clap/server/adapter/outbound/persistense/entity/task/TaskEntity.java b/src/main/java/clap/server/adapter/outbound/persistense/entity/task/TaskEntity.java index 1dfe9961..79f6d60f 100644 --- a/src/main/java/clap/server/adapter/outbound/persistense/entity/task/TaskEntity.java +++ b/src/main/java/clap/server/adapter/outbound/persistense/entity/task/TaskEntity.java @@ -42,8 +42,8 @@ public class TaskEntity extends BaseTimeEntity { @Enumerated(EnumType.STRING) private TaskStatus taskStatus; - @Column(nullable = false) - private int processorOrder; + @Column + private Long processorOrder; @ManyToOne(fetch = FetchType.LAZY) @JoinColumn(name = "reviewer_id") @@ -62,4 +62,7 @@ public class TaskEntity extends BaseTimeEntity { @Column private LocalDateTime completedAt; + + @Column + private LocalDateTime finishedAt; } diff --git a/src/main/java/clap/server/adapter/outbound/persistense/entity/task/constant/TaskStatus.java b/src/main/java/clap/server/adapter/outbound/persistense/entity/task/constant/TaskStatus.java index 2bb4f027..29972c3d 100644 --- a/src/main/java/clap/server/adapter/outbound/persistense/entity/task/constant/TaskStatus.java +++ b/src/main/java/clap/server/adapter/outbound/persistense/entity/task/constant/TaskStatus.java @@ -10,7 +10,7 @@ public enum TaskStatus { REQUESTED("요청"), IN_PROGRESS("진행 중"), - PENDING_COMPLETED("검토 중"), + PENDING_COMPLETED("완료 대기"), COMPLETED("완료"), TERMINATED("종료"); diff --git a/src/main/java/clap/server/adapter/outbound/persistense/mapper/AttachmentPersistenceMapper.java b/src/main/java/clap/server/adapter/outbound/persistense/mapper/AttachmentPersistenceMapper.java index 70c1f4a9..bc8ae721 100644 --- a/src/main/java/clap/server/adapter/outbound/persistense/mapper/AttachmentPersistenceMapper.java +++ b/src/main/java/clap/server/adapter/outbound/persistense/mapper/AttachmentPersistenceMapper.java @@ -4,7 +4,16 @@ import clap.server.adapter.outbound.persistense.mapper.common.PersistenceMapper; import clap.server.domain.model.task.Attachment; import org.mapstruct.Mapper; +import org.mapstruct.Mapping; @Mapper(componentModel = "spring", uses = {TaskPersistenceMapper.class, CommentPersistenceMapper.class}) public interface AttachmentPersistenceMapper extends PersistenceMapper { + @Override + @Mapping(source = "deleted", target = "isDeleted") + Attachment toDomain(final AttachmentEntity attachment); + + @Override + @Mapping(source = "deleted", target = "isDeleted") + AttachmentEntity toEntity(final Attachment Attachment); + } diff --git a/src/main/java/clap/server/adapter/outbound/persistense/repository/task/AttachmentRepository.java b/src/main/java/clap/server/adapter/outbound/persistense/repository/task/AttachmentRepository.java index 60e708f5..ed92d792 100644 --- a/src/main/java/clap/server/adapter/outbound/persistense/repository/task/AttachmentRepository.java +++ b/src/main/java/clap/server/adapter/outbound/persistense/repository/task/AttachmentRepository.java @@ -3,13 +3,11 @@ import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.stereotype.Repository; -import java.util.Collection; import java.util.List; @Repository public interface AttachmentRepository extends JpaRepository { - List findAllByTask_TaskIdAndCommentIsNull(Long taskId); - void deleteAllByAttachmentIdIn(List attachmentIds); - List findAllByTask_TaskIdAndCommentIsNullAndAttachmentIdIn(Long task_taskId, List attachmentId); + List findAllByTask_TaskIdAndCommentIsNullAndIsDeletedIsFalse(Long taskId); + List findAllByTask_TaskIdAndCommentIsNullAndIsDeletedIsFalseAndAttachmentIdIn(Long task_taskId, List attachmentId); } \ No newline at end of file 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 97e1363e..c5bacd86 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 @@ -2,7 +2,10 @@ import clap.server.adapter.outbound.persistense.entity.task.TaskEntity; +import clap.server.adapter.outbound.persistense.entity.task.constant.TaskStatus; import io.lettuce.core.dynamic.annotation.Param; +import org.springframework.data.domain.Pageable; +import org.springframework.data.domain.Slice; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.Query; import org.springframework.stereotype.Repository; @@ -20,6 +23,19 @@ List findYesterdayTaskByUpdatedAtIsBetween( @Param("updatedAtAfter") LocalDateTime updatedAtAfter, @Param("updatedAtBefore") LocalDateTime updatedAtBefore ); + + + @Query("SELECT t FROM TaskEntity t WHERE t.processor.memberId = :processorId " + + "AND t.taskStatus IN :taskStatus " + + "AND (t.taskStatus != 'COMPLETED' OR t.completedAt >= :untilDate)") + Slice findTasksWithTaskStatusAndCompletedAt( + @Param("processorId") Long processorId, + @Param("taskStatus") List taskStatus, + @Param("untilDate") LocalDateTime untilDate, + Pageable pageable + ); + + } diff --git a/src/main/java/clap/server/application/Task/CreateTaskService.java b/src/main/java/clap/server/application/Task/CreateTaskService.java index e529fc4e..c20c03f3 100644 --- a/src/main/java/clap/server/application/Task/CreateTaskService.java +++ b/src/main/java/clap/server/application/Task/CreateTaskService.java @@ -49,6 +49,8 @@ public CreateTaskResponse createTask(Long requesterId, CreateTaskRequest createT Category category = categoryService.findById(createTaskRequest.categoryId()); Task task = Task.createTask(member, category, createTaskRequest.title(), createTaskRequest.description()); Task savedTask = commandTaskPort.save(task); + savedTask.setInitialProcessorOrder(); + commandTaskPort.save(savedTask); saveAttachments(files, savedTask); publishNotification(savedTask); diff --git a/src/main/java/clap/server/application/Task/TaskBoardService.java b/src/main/java/clap/server/application/Task/TaskBoardService.java new file mode 100644 index 00000000..59c81fa6 --- /dev/null +++ b/src/main/java/clap/server/application/Task/TaskBoardService.java @@ -0,0 +1,38 @@ +package clap.server.application.Task; + +import clap.server.adapter.inbound.web.dto.task.response.TaskBoardResponse; +import clap.server.adapter.outbound.persistense.entity.task.constant.TaskStatus; +import clap.server.application.mapper.TaskMapper; +import clap.server.application.port.inbound.domain.MemberService; +import clap.server.application.port.inbound.task.TaskBoardUsecase; +import clap.server.application.port.outbound.task.LoadTaskPort; +import clap.server.common.annotation.architecture.ApplicationService; +import clap.server.domain.model.task.Task; +import lombok.RequiredArgsConstructor; +import org.springframework.data.domain.Pageable; +import org.springframework.data.domain.Slice; +import org.springframework.transaction.annotation.Transactional; + +import java.time.LocalDate; +import java.util.List; + +@ApplicationService +@RequiredArgsConstructor +class TaskBoardService implements TaskBoardUsecase { + private final static List VIEWABLE_STATUSES = List.of( + TaskStatus.IN_PROGRESS, + TaskStatus.PENDING_COMPLETED, + TaskStatus.COMPLETED + ); + private final MemberService memberService; + private final LoadTaskPort loadTaskPort; + + @Transactional(readOnly = true) + @Override + public TaskBoardResponse getTaskBoards(Long processorId, LocalDate untilDate, Pageable pageable) { + memberService.findById(processorId); + Slice tasks = loadTaskPort.findByProcessorAndStatus(processorId, VIEWABLE_STATUSES, untilDate.plusDays(1).atStartOfDay(), pageable); + return TaskMapper.toSliceTaskItemResponse(tasks); + } + +} diff --git a/src/main/java/clap/server/application/Task/UpdateTaskService.java b/src/main/java/clap/server/application/Task/UpdateTaskService.java index e25938ac..8ea53487 100644 --- a/src/main/java/clap/server/application/Task/UpdateTaskService.java +++ b/src/main/java/clap/server/application/Task/UpdateTaskService.java @@ -28,7 +28,7 @@ import org.springframework.web.multipart.MultipartFile; import java.util.List; - +import java.util.Objects; @ApplicationService @@ -47,13 +47,15 @@ public class UpdateTaskService implements UpdateTaskUsecase { @Override @Transactional public UpdateTaskResponse updateTask(Long requesterId, Long taskId, UpdateTaskRequest updateTaskRequest, List files) { - memberService.findActiveMember(requesterId); + Member requester = memberService.findActiveMember(requesterId); Category category = categoryService.findById(updateTaskRequest.categoryId()); Task task = taskService.findById(taskId); - if(task.getTaskStatus() != TaskStatus.REQUESTED){ + + if(!Objects.equals(requester.getMemberId(), task.getRequester().getMemberId())) { throw new ApplicationException(TaskErrorCode.TASK_STATUS_MISMATCH); } - task.updateTask(category, updateTaskRequest.title(), updateTaskRequest.description()); + + task.updateTask(task.getTaskStatus(), category, updateTaskRequest.title(), updateTaskRequest.description()); Task updatedTask = commandTaskPort.save(task); if (!updateTaskRequest.attachmentsToDelete().isEmpty()){ @@ -63,18 +65,19 @@ public UpdateTaskResponse updateTask(Long requesterId, Long taskId, UpdateTaskRe } private void updateAttachments(List attachmentIdsToDelete, List files, Task task) { - validateAttachments(attachmentIdsToDelete, task); - commandAttachmentPort.deleteByIds(attachmentIdsToDelete); + List attachmentsToDelete = validateAndGetAttachments(attachmentIdsToDelete, task); + attachmentsToDelete.forEach(Attachment::softDelete); List fileUrls = s3UploadAdapter.uploadFiles(FilePath.TASK_IMAGE, files); List attachments = AttachmentMapper.toTaskAttachments(task, files, fileUrls); commandAttachmentPort.saveAll(attachments); } - private void validateAttachments(List attachmentIdsToDelete, Task task) { + private List validateAndGetAttachments(List attachmentIdsToDelete, Task task) { List attachmentsOfTask = loadAttachmentPort.findAllByTaskIdAndCommentIsNullAndAttachmentId(task.getTaskId(), attachmentIdsToDelete); if(attachmentsOfTask.size() != attachmentIdsToDelete.size()) { throw new ApplicationException(TaskErrorCode.TASK_ATTACHMENT_NOT_FOUND); } + return attachmentsOfTask; } } diff --git a/src/main/java/clap/server/application/mapper/NotificationMapper.java b/src/main/java/clap/server/application/mapper/NotificationMapper.java index d53d71b4..e2c83e52 100644 --- a/src/main/java/clap/server/application/mapper/NotificationMapper.java +++ b/src/main/java/clap/server/application/mapper/NotificationMapper.java @@ -23,8 +23,7 @@ public static FindNotificationListResponse toFindNoticeListResponse(Notification public static SliceResponse toSliceOfFindNoticeListResponse(Slice slice) { return new SliceResponse<>( slice.getContent(), - slice.getNumber(), - slice.getSize(), + slice.hasNext(), slice.isFirst(), slice.isLast() ); diff --git a/src/main/java/clap/server/application/mapper/TaskMapper.java b/src/main/java/clap/server/application/mapper/TaskMapper.java index 542df775..77eb6df2 100644 --- a/src/main/java/clap/server/application/mapper/TaskMapper.java +++ b/src/main/java/clap/server/application/mapper/TaskMapper.java @@ -2,10 +2,16 @@ import clap.server.adapter.inbound.web.dto.task.*; +import clap.server.adapter.inbound.web.dto.task.response.TaskBoardResponse; +import clap.server.adapter.inbound.web.dto.task.response.TaskItemResponse; +import clap.server.adapter.outbound.persistense.entity.task.constant.TaskStatus; import clap.server.domain.model.task.Attachment; import clap.server.domain.model.task.Task; +import org.springframework.data.domain.Slice; +import java.util.Collections; import java.util.List; +import java.util.Map; import java.util.stream.Collectors; public class TaskMapper { @@ -31,7 +37,7 @@ public static FilterTaskListResponse toFilterTaskListResponse(Task task) { task.getTitle(), task.getProcessor() != null ? task.getProcessor().getMemberInfo().getNickname() : "", task.getTaskStatus(), - task.getCompletedAt() != null ? task.getCompletedAt() : null + task.getFinishedAt() != null ? task.getFinishedAt() : null ); } @@ -63,7 +69,7 @@ public static FindTaskDetailsResponse toFindTaskDetailResponse(Task task, List tasks) { + Map> tasksByStatus =tasks.getContent().stream() + .map(TaskMapper::toTaskItemResponse) + .collect(Collectors.groupingBy(TaskItemResponse::taskStatus)); + + return new TaskBoardResponse( + tasksByStatus.getOrDefault(TaskStatus.IN_PROGRESS, Collections.emptyList()), + tasksByStatus.getOrDefault(TaskStatus.PENDING_COMPLETED, Collections.emptyList()), + tasksByStatus.getOrDefault(TaskStatus.COMPLETED, Collections.emptyList()), + tasks.hasNext(), + tasks.isFirst(), + tasks.isLast() + ); + } + + private static TaskItemResponse toTaskItemResponse(Task task) { + return new TaskItemResponse( + task.getTaskId(), + task.getTaskCode(), + task.getCategory().getMainCategory().getName(), + task.getCategory().getName(), + task.getRequester().getNickname(), + task.getRequester().getImageUrl(), + task.getRequester().getMemberInfo().getDepartment().getName(), + task.getProcessorOrder(), + task.getTaskStatus(), + task.getCreatedAt() + ); + } } diff --git a/src/main/java/clap/server/application/port/inbound/task/TaskBoardUsecase.java b/src/main/java/clap/server/application/port/inbound/task/TaskBoardUsecase.java new file mode 100644 index 00000000..073f94b9 --- /dev/null +++ b/src/main/java/clap/server/application/port/inbound/task/TaskBoardUsecase.java @@ -0,0 +1,10 @@ +package clap.server.application.port.inbound.task; + +import clap.server.adapter.inbound.web.dto.task.response.TaskBoardResponse; +import org.springframework.data.domain.Pageable; + +import java.time.LocalDate; + +public interface TaskBoardUsecase { + TaskBoardResponse getTaskBoards(Long processorId, LocalDate untilDate, Pageable pageable); +} \ No newline at end of file diff --git a/src/main/java/clap/server/application/port/outbound/task/CommandAttachmentPort.java b/src/main/java/clap/server/application/port/outbound/task/CommandAttachmentPort.java index ff7a6e8a..c7dc94d9 100644 --- a/src/main/java/clap/server/application/port/outbound/task/CommandAttachmentPort.java +++ b/src/main/java/clap/server/application/port/outbound/task/CommandAttachmentPort.java @@ -3,12 +3,9 @@ import clap.server.domain.model.task.Attachment; import java.util.List; -import java.util.Optional; public interface CommandAttachmentPort { void save(Attachment attachment); void saveAll(List attachments); - - void deleteByIds(List attachmentId); } 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 50042219..721cb723 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 @@ -1,11 +1,13 @@ package clap.server.application.port.outbound.task; +import clap.server.adapter.inbound.web.dto.task.FilterPendingApprovalResponse; import clap.server.adapter.inbound.web.dto.task.FilterTaskListRequest; import clap.server.adapter.inbound.web.dto.task.FilterTaskListResponse; -import clap.server.adapter.inbound.web.dto.task.FilterPendingApprovalResponse; +import clap.server.adapter.outbound.persistense.entity.task.constant.TaskStatus; import clap.server.domain.model.task.Task; import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; +import org.springframework.data.domain.Slice; import java.time.LocalDateTime; import java.util.List; @@ -20,4 +22,5 @@ public interface LoadTaskPort { Page findPendingApprovalTasks(Pageable pageable, FilterTaskListRequest filterTaskListRequest); + Slice findByProcessorAndStatus(Long processorId, List statuses, LocalDateTime untilDate, Pageable pageable); } \ No newline at end of file diff --git a/src/main/java/clap/server/domain/model/task/Attachment.java b/src/main/java/clap/server/domain/model/task/Attachment.java index 1646eada..3fafddee 100644 --- a/src/main/java/clap/server/domain/model/task/Attachment.java +++ b/src/main/java/clap/server/domain/model/task/Attachment.java @@ -17,14 +17,16 @@ public class Attachment extends BaseTime { private String originalName; private String fileUrl; private String fileSize; + private boolean isDeleted; @Builder - public Attachment(Task task, Comment comment, String originalName, String fileUrl, String fileSize) { + public Attachment(Task task, Comment comment, String originalName, String fileUrl, String fileSize, boolean isDeleted) { this.task = task; this.comment = comment; this.originalName = originalName; this.fileUrl = fileUrl; this.fileSize = fileSize; + this.isDeleted = isDeleted; } public static Attachment createAttachment(Task task, String originalName, String fileUrl, long fileSize) { @@ -34,9 +36,14 @@ public static Attachment createAttachment(Task task, String originalName, String .originalName(originalName) .fileUrl(fileUrl) .fileSize(formatFileSize(fileSize)) + .isDeleted(false) .build(); } + public void softDelete() { + this.isDeleted = true; + } + public static String formatFileSize(long size) { if (size < 1024) return size + " B"; int z = (63 - Long.numberOfLeadingZeros(size)) / 10; diff --git a/src/main/java/clap/server/domain/model/task/Task.java b/src/main/java/clap/server/domain/model/task/Task.java index 1142fba0..3b936381 100644 --- a/src/main/java/clap/server/domain/model/task/Task.java +++ b/src/main/java/clap/server/domain/model/task/Task.java @@ -3,16 +3,16 @@ import clap.server.adapter.outbound.persistense.entity.task.constant.TaskStatus; import clap.server.domain.model.common.BaseTime; import clap.server.domain.model.member.Member; -import clap.server.exception.ApplicationException; -import clap.server.exception.code.MemberErrorCode; +import clap.server.exception.DomainException; +import clap.server.exception.code.TaskErrorCode; import lombok.AccessLevel; -import lombok.Builder; import lombok.Getter; import lombok.NoArgsConstructor; import lombok.experimental.SuperBuilder; import java.time.LocalDateTime; import java.time.format.DateTimeFormatter; +import java.util.Objects; @Getter @SuperBuilder @@ -25,12 +25,12 @@ public class Task extends BaseTime { private Category category; private Member requester; private TaskStatus taskStatus; - private int processorOrder; + private long processorOrder; private Member processor; private Label label; private Member reviewer; private LocalDateTime dueDate; - private LocalDateTime completedAt; + private LocalDateTime finishedAt; public static Task createTask(Member member, Category category, String title, String description) { return Task.builder() @@ -43,13 +43,22 @@ public static Task createTask(Member member, Category category, String title, St .build(); } - public void updateTask(Category category, String title, String description) { + public void updateTask(TaskStatus status, Category category, String title, String description) { + if (status != TaskStatus.REQUESTED) { + throw new DomainException(TaskErrorCode.TASK_STATUS_MISMATCH); + } this.category = category; this.title = title; this.description = description; this.taskCode = toTaskCode(category); } + public void setInitialProcessorOrder() { + if(this.processor == null) { + this.processorOrder = this.taskId * 128L; + } + } + public void approveTask(Member reviewer, Member processor, LocalDateTime dueDate, Category category, Label label) { this.reviewer = reviewer; this.processor = processor; diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index f4527cd4..9fb3c601 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -9,6 +9,7 @@ spring: - auth.yml - elasticsearch.yml - s3.yml + application: name: taskflow web.resources.add-mappings: false @@ -27,8 +28,8 @@ server: web: domain: - local: ${TASKFLOW_LOCAL_WEB:127.0.0.1:3O00} - service: ${TASKFLOW_SERVICE_WEB:127.0.0.1:3000} + local: ${TASKFLOW_LOCAL_WEB:127.0.0.1:5173} + service: ${TASKFLOW_SERVICE_WEB:127.0.0.1:5173} #logging: # level: # root: INFO