diff --git a/src/main/java/clap/server/adapter/inbound/web/dto/task/request/FilterTaskBoardRequest.java b/src/main/java/clap/server/adapter/inbound/web/dto/task/request/FilterTaskBoardRequest.java index bde8cfe4..758e8720 100644 --- a/src/main/java/clap/server/adapter/inbound/web/dto/task/request/FilterTaskBoardRequest.java +++ b/src/main/java/clap/server/adapter/inbound/web/dto/task/request/FilterTaskBoardRequest.java @@ -1,16 +1,21 @@ package clap.server.adapter.inbound.web.dto.task.request; import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotNull; + +import java.util.List; public record FilterTaskBoardRequest( @Schema(description = "라벨 ID") Long labelId, - @Schema(description = "1차 카테고리 ID, ** 2차 카테고리로 검색할 시에는 1차 카테고리 값은 넣지 않습니다.") - Long mainCategoryId, + @Schema(description = "1차 카테고리 ID 목록", example = "[1, 3, 5]") + @NotNull + List mainCategoryIds, - @Schema(description = "2차 카테고리 ID") - Long subCategoryId, + @Schema(description = "2차 카테고리 ID 목록", example = "[2, 4]") + @NotNull + List categoryIds, @Schema(description = "작업 제목", example = "작업 제목") String title, 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 index 1c973647..f1953e11 100644 --- 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 @@ -5,9 +5,6 @@ public record TaskBoardResponse( List tasksInProgress, List tasksPendingComplete, - List tasksCompleted, - boolean hasNext, - boolean isFirst, - boolean isLast + List tasksCompleted ){ } 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 index 7153ba95..44570e00 100644 --- a/src/main/java/clap/server/adapter/inbound/web/task/TaskBoardController.java +++ b/src/main/java/clap/server/adapter/inbound/web/task/TaskBoardController.java @@ -7,7 +7,6 @@ import clap.server.adapter.outbound.persistense.entity.task.constant.TaskStatus; import clap.server.application.port.inbound.task.FilterTaskBoardUsecase; import clap.server.application.port.inbound.task.UpdateTaskBoardUsecase; -import clap.server.application.port.inbound.task.GetTaskBoardUsecase; import clap.server.application.port.inbound.task.UpdateTaskOrderAndStatusUsecase; import clap.server.common.annotation.architecture.WebAdapter; import io.swagger.v3.oas.annotations.Operation; @@ -15,9 +14,6 @@ import io.swagger.v3.oas.annotations.media.Schema; import io.swagger.v3.oas.annotations.tags.Tag; import lombok.RequiredArgsConstructor; -import lombok.extern.slf4j.Slf4j; -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.access.annotation.Secured; @@ -26,14 +22,12 @@ import java.time.LocalDate; -@Slf4j @Tag(name = "02. Task [담당자]") @WebAdapter @RestController @RequiredArgsConstructor @RequestMapping("/api/task-board") public class TaskBoardController { - private final GetTaskBoardUsecase getTaskBoardUsecase; private final FilterTaskBoardUsecase filterTaskBoardUsecase; private final UpdateTaskBoardUsecase updateTaskBoardUsecase; private final UpdateTaskOrderAndStatusUsecase updateTaskOrderAndStatus; @@ -41,23 +35,19 @@ public class TaskBoardController { @Operation(summary = "작업 보드 조회 API") @Secured({"ROLE_MANAGER"}) @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, - @io.swagger.v3.oas.annotations.parameters.RequestBody(description = "필터링 조회 request") @ModelAttribute FilterTaskBoardRequest request, - @AuthenticationPrincipal SecurityUserDetails userInfo) { - Pageable pageable = PageRequest.of(page, pageSize); - if (request != null) { - return ResponseEntity.ok(filterTaskBoardUsecase.getTaskBoardByFilter(userInfo.getUserId(), untilDate, request, pageable)); - } else - return ResponseEntity.ok(getTaskBoardUsecase.getTaskBoards(userInfo.getUserId(), untilDate, pageable)); + public ResponseEntity getTaskBoard( + @Parameter(description = "작업 완료 일자 조회 기준, yyyy-mm-dd 형식으로 입력합니다.") @RequestParam(required = false) + @DateTimeFormat(pattern = "yyyy-MM-dd") LocalDate fromDate, + @ModelAttribute FilterTaskBoardRequest request, + @AuthenticationPrincipal SecurityUserDetails userInfo) { + return ResponseEntity.ok(filterTaskBoardUsecase.getTaskBoardByFilter(userInfo.getUserId(), fromDate, request)); } @Operation(summary = "작업 보드 순서 및 상태 변경 API") @Secured({"ROLE_MANAGER"}) @PatchMapping - public void updateTaskBoard(@Parameter(description = "전환될 작업의 상태, 상태 전환이 아니라면 입력 X", schema = @Schema(allowableValues = {"IN_PROGRESS", "PENDING_COMPLETED", "COMPLETED"})) + public void updateTaskBoard(@Parameter(description = "전환될 작업의 상태, 상태 전환이 아니라면 입력 X", + schema = @Schema(allowableValues = {"IN_PROGRESS", "PENDING_COMPLETED", "COMPLETED"})) @RequestParam(required = false) TaskStatus status, @RequestBody UpdateTaskOrderRequest request, @AuthenticationPrincipal SecurityUserDetails userInfo) { 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 7171871b..152cc704 100644 --- a/src/main/java/clap/server/adapter/outbound/persistense/TaskPersistenceAdapter.java +++ b/src/main/java/clap/server/adapter/outbound/persistense/TaskPersistenceAdapter.java @@ -1,7 +1,7 @@ package clap.server.adapter.outbound.persistense; -import clap.server.adapter.inbound.web.dto.task.request.FilterTaskListRequest; import clap.server.adapter.inbound.web.dto.task.request.FilterTaskBoardRequest; +import clap.server.adapter.inbound.web.dto.task.request.FilterTaskListRequest; import clap.server.adapter.inbound.web.dto.task.request.FilterTeamStatusRequest; import clap.server.adapter.inbound.web.dto.task.response.TeamMemberTaskResponse; import clap.server.adapter.outbound.persistense.entity.task.TaskEntity; @@ -16,8 +16,6 @@ import lombok.extern.slf4j.Slf4j; import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; -import org.springframework.data.domain.Slice; -import org.springframework.data.domain.SliceImpl; import java.time.LocalDateTime; import java.util.List; @@ -62,9 +60,9 @@ public Page findPendingApprovalTasks(Pageable pageable, FilterTaskListRequ } @Override - public Slice findByProcessorAndStatus(Long processorId, List statuses, LocalDateTime untilDate, Pageable pageable) { - Slice tasks = taskRepository.findTasksWithTaskStatusAndCompletedAt(processorId, statuses, untilDate, pageable); - return tasks.map(taskPersistenceMapper::toDomain); + public List findByProcessorAndStatus(Long processorId, List statuses, LocalDateTime fromDateTime) { + List tasks = taskRepository.findTasksWithTaskStatusAndCompletedAt(processorId, statuses, fromDateTime); + return tasks.stream().map(taskPersistenceMapper::toDomain).toList(); } @Override @@ -98,17 +96,10 @@ public Optional findNextOrderTaskByProcessorIdAndStatus(Long processorId, } @Override - public Slice findTaskBoardByFilter(Long processorId, List statuses, LocalDateTime untilDate, FilterTaskBoardRequest request, Pageable pageable) { - List taskList = new java.util.ArrayList<>(taskRepository.findTasksByFilter(processorId, statuses, untilDate, request, pageable) + public List findTaskBoardByFilter(Long processorId, List statuses, LocalDateTime fromDate, FilterTaskBoardRequest request) { + return taskRepository.findTasksByFilter(processorId, statuses, fromDate, request) .stream() - .map(taskPersistenceMapper::toDomain) - .toList()); - - boolean hasNext = taskList.size() > pageable.getPageSize(); - if (hasNext) { - taskList.remove(taskList.size() - 1); - } - return new SliceImpl<>(taskList, pageable, hasNext); + .map(taskPersistenceMapper::toDomain).toList(); } @Override diff --git a/src/main/java/clap/server/adapter/outbound/persistense/repository/task/TaskCustomRepository.java b/src/main/java/clap/server/adapter/outbound/persistense/repository/task/TaskCustomRepository.java index b016c5cb..aa0793f2 100644 --- a/src/main/java/clap/server/adapter/outbound/persistense/repository/task/TaskCustomRepository.java +++ b/src/main/java/clap/server/adapter/outbound/persistense/repository/task/TaskCustomRepository.java @@ -18,6 +18,6 @@ public interface TaskCustomRepository { List findTeamStatus(Long memberId, FilterTeamStatusRequest filter); Page findPendingApprovalTasks(Pageable pageable, FilterTaskListRequest findTaskListRequest); Page findAllTasks(Pageable pageable, FilterTaskListRequest findTaskListRequest); - List findTasksByFilter(Long processorId, List statuses, LocalDateTime localDateTime, FilterTaskBoardRequest request, Pageable pageable); + List findTasksByFilter(Long processorId, List statuses, LocalDateTime localDateTime, FilterTaskBoardRequest request); Page findTasksAssignedByManager(Long processorId, Pageable pageable, FilterTaskListRequest findTaskListRequest); } diff --git a/src/main/java/clap/server/adapter/outbound/persistense/repository/task/TaskCustomRepositoryImpl.java b/src/main/java/clap/server/adapter/outbound/persistense/repository/task/TaskCustomRepositoryImpl.java index 84e8880c..94459a7a 100644 --- a/src/main/java/clap/server/adapter/outbound/persistense/repository/task/TaskCustomRepositoryImpl.java +++ b/src/main/java/clap/server/adapter/outbound/persistense/repository/task/TaskCustomRepositoryImpl.java @@ -32,7 +32,6 @@ public class TaskCustomRepositoryImpl implements TaskCustomRepository { private final JPAQueryFactory queryFactory; - private final EntityManager entityManager; @Override public Page findTasksRequestedByUser(Long requesterId, Pageable pageable, FilterTaskListRequest filterTaskListRequest) { @@ -58,7 +57,6 @@ public Page findTasksAssignedByManager(Long processorId, Pageable pa @Override public List findTeamStatus(Long memberId, FilterTeamStatusRequest filter) { - // 1. 담당자 목록을 가져옴 (페이징 제거) List processorIds = queryFactory .select(taskEntity.processor.memberId) .from(taskEntity) @@ -176,32 +174,37 @@ public Page findAllTasks(Pageable pageable, FilterTaskListRequest fi } @Override - public List findTasksByFilter(Long processorId, List statuses, LocalDateTime untilDateTime, FilterTaskBoardRequest request, Pageable pageable) { - BooleanBuilder builder = createTaskBoardFilter(processorId, statuses, untilDateTime, request); + public List findTasksByFilter(Long processorId, List statuses, LocalDateTime fromDateTime, FilterTaskBoardRequest request) { + BooleanBuilder builder = createTaskBoardFilter(processorId, statuses, fromDateTime, request); return queryFactory .selectFrom(taskEntity) .where(builder) .orderBy(taskEntity.processorOrder.asc()) - .limit(pageable.getPageSize() + 1) - .offset(pageable.getOffset()) .fetch(); } - private BooleanBuilder createTaskBoardFilter(Long processorId, List statuses, LocalDateTime untilDateTime, FilterTaskBoardRequest request) { + private BooleanBuilder createTaskBoardFilter(Long processorId, List statuses, LocalDateTime fromDateTime, FilterTaskBoardRequest request) { BooleanBuilder builder = new BooleanBuilder(); builder.and(taskEntity.processor.memberId.eq(processorId)); builder.and(taskEntity.taskStatus.in(statuses)); - builder.and(taskEntity.finishedAt.isNull().or(taskEntity.finishedAt.loe(untilDateTime))); + + if (fromDateTime != null) { + builder.and(taskEntity.taskStatus.ne(TaskStatus.COMPLETED) + .or(taskEntity.taskStatus.eq(TaskStatus.COMPLETED) + .and(taskEntity.finishedAt.goe(fromDateTime)))); + } if (request.labelId() != null) { builder.and(taskEntity.label.labelId.eq(request.labelId())); } - if (request.mainCategoryId() != null) { - builder.and(taskEntity.category.mainCategory.categoryId.eq(request.mainCategoryId())); + if (!request.mainCategoryIds().isEmpty()) { + builder.and(taskEntity.category.mainCategory.categoryId.in(request.mainCategoryIds()) + .and(taskEntity.category.mainCategory.isDeleted.eq(false))); } - if (request.subCategoryId() != null) { - builder.and(taskEntity.category.categoryId.eq(request.subCategoryId())); + if (!request.categoryIds().isEmpty()) { + builder.and(taskEntity.category.categoryId.in(request.categoryIds()) + .and(taskEntity.category.isDeleted.eq(false))); } if (request.title() != null && !request.title().isEmpty()) { String titleFilter = "%" + request.title() + "%"; 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 adf87e22..27f09d4b 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 @@ -38,15 +38,16 @@ List findYesterdayTaskByUpdatedAtIsBetween( @Query("SELECT t FROM TaskEntity t " + "WHERE t.processor.memberId = :processorId " + "AND t.taskStatus IN :taskStatus " + - "AND (t.taskStatus != 'COMPLETED' OR t.finishedAt <= :untilDate) " + + "AND (:fromDateTime IS NULL OR t.taskStatus != 'COMPLETED' OR " + + " (t.taskStatus = 'COMPLETED' AND t.finishedAt >= :fromDateTime)) " + "ORDER BY t.processorOrder ASC ") - Slice findTasksWithTaskStatusAndCompletedAt( + List findTasksWithTaskStatusAndCompletedAt( @Param("processorId") Long processorId, @Param("taskStatus") List taskStatus, - @Param("untilDate") LocalDateTime untilDate, - Pageable pageable + @Param("fromDateTime") LocalDateTime fromDateTime ); + Optional findByTaskIdAndTaskStatus(Long id, TaskStatus status); Optional findTopByProcessor_MemberIdAndTaskStatusAndProcessorOrderLessThanOrderByProcessorOrderDesc(Long processorId, TaskStatus taskStatus, Long processorOrder); diff --git a/src/main/java/clap/server/application/mapper/TaskResponseMapper.java b/src/main/java/clap/server/application/mapper/TaskResponseMapper.java index 7381a5ed..d18ec909 100644 --- a/src/main/java/clap/server/application/mapper/TaskResponseMapper.java +++ b/src/main/java/clap/server/application/mapper/TaskResponseMapper.java @@ -119,18 +119,15 @@ public static FilterAllTasksResponse toFilterAllTasksResponse(Task task) { ); } - public static TaskBoardResponse toSliceTaskItemResponse(Slice tasks) { - Map> tasksByStatus =tasks.getContent().stream() + public static TaskBoardResponse toTaskBoardResponse(List tasks) { + Map> tasksByStatus =tasks.stream() .map(TaskResponseMapper::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() + tasksByStatus.getOrDefault(TaskStatus.COMPLETED, Collections.emptyList()) ); } diff --git a/src/main/java/clap/server/application/port/inbound/task/FilterTaskBoardUsecase.java b/src/main/java/clap/server/application/port/inbound/task/FilterTaskBoardUsecase.java index a245ca28..7c096a26 100644 --- a/src/main/java/clap/server/application/port/inbound/task/FilterTaskBoardUsecase.java +++ b/src/main/java/clap/server/application/port/inbound/task/FilterTaskBoardUsecase.java @@ -7,5 +7,5 @@ import java.time.LocalDate; public interface FilterTaskBoardUsecase { - TaskBoardResponse getTaskBoardByFilter(Long processorId, LocalDate untilDate, FilterTaskBoardRequest request, Pageable pageable); + TaskBoardResponse getTaskBoardByFilter(Long processorId, LocalDate fromDate, FilterTaskBoardRequest request); } diff --git a/src/main/java/clap/server/application/port/inbound/task/GetTaskBoardUsecase.java b/src/main/java/clap/server/application/port/inbound/task/GetTaskBoardUsecase.java deleted file mode 100644 index ccf7dce9..00000000 --- a/src/main/java/clap/server/application/port/inbound/task/GetTaskBoardUsecase.java +++ /dev/null @@ -1,10 +0,0 @@ -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 GetTaskBoardUsecase { - 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/LoadTaskPort.java b/src/main/java/clap/server/application/port/outbound/task/LoadTaskPort.java index 054d0343..3d00f2d2 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,14 +1,13 @@ package clap.server.application.port.outbound.task; -import clap.server.adapter.inbound.web.dto.task.request.FilterTaskListRequest; import clap.server.adapter.inbound.web.dto.task.request.FilterTaskBoardRequest; +import clap.server.adapter.inbound.web.dto.task.request.FilterTaskListRequest; import clap.server.adapter.inbound.web.dto.task.request.FilterTeamStatusRequest; import clap.server.adapter.inbound.web.dto.task.response.TeamMemberTaskResponse; 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; @@ -27,7 +26,7 @@ public interface LoadTaskPort { Page findAllTasks(Pageable pageable, FilterTaskListRequest findTaskListRequest); - Slice findByProcessorAndStatus(Long processorId, List statuses, LocalDateTime untilDate, Pageable pageable); + List findByProcessorAndStatus(Long processorId, List statuses, LocalDateTime fromDate); Optional findByIdAndStatus(Long id, TaskStatus status); @@ -35,7 +34,7 @@ public interface LoadTaskPort { Optional findNextOrderTaskByProcessorIdAndStatus(Long processorId, TaskStatus taskStatus, Long processorOrder); - Slice findTaskBoardByFilter(Long processorId, List statuses, LocalDateTime untilDateTime, FilterTaskBoardRequest request, Pageable pageable); + List findTaskBoardByFilter(Long processorId, List statuses, LocalDateTime untilDateTime, FilterTaskBoardRequest request); List findTeamStatus(Long memberId, FilterTeamStatusRequest filter); } diff --git a/src/main/java/clap/server/application/service/task/GetTaskBoardService.java b/src/main/java/clap/server/application/service/task/GetTaskBoardService.java index 3f126e17..1427d140 100644 --- a/src/main/java/clap/server/application/service/task/GetTaskBoardService.java +++ b/src/main/java/clap/server/application/service/task/GetTaskBoardService.java @@ -5,15 +5,12 @@ import clap.server.application.mapper.TaskResponseMapper; import clap.server.application.port.inbound.domain.MemberService; import clap.server.application.port.inbound.task.FilterTaskBoardUsecase; -import clap.server.application.port.inbound.task.GetTaskBoardUsecase; import clap.server.application.port.outbound.task.LoadTaskPort; import clap.server.common.annotation.architecture.ApplicationService; import clap.server.domain.model.task.Task; import clap.server.domain.policy.task.TaskPolicyConstants; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; -import org.springframework.data.domain.Pageable; -import org.springframework.data.domain.Slice; import org.springframework.transaction.annotation.Transactional; import java.time.LocalDate; @@ -24,23 +21,15 @@ @ApplicationService @RequiredArgsConstructor @Transactional(readOnly = true) -class GetTaskBoardService implements GetTaskBoardUsecase, FilterTaskBoardUsecase { +class GetTaskBoardService implements FilterTaskBoardUsecase { private final MemberService memberService; private final LoadTaskPort loadTaskPort; @Override - public TaskBoardResponse getTaskBoards(Long processorId, LocalDate untilDate, Pageable pageable) { + public TaskBoardResponse getTaskBoardByFilter(Long processorId, LocalDate fromDate, FilterTaskBoardRequest request) { memberService.findActiveMember(processorId); - LocalDateTime untilDateTime = untilDate == null ? LocalDate.now().plusDays(1).atStartOfDay() : untilDate.plusDays(1).atStartOfDay(); - Slice tasks = loadTaskPort.findByProcessorAndStatus(processorId, TaskPolicyConstants.TASK_BOARD_STATUS_FILTER, untilDateTime, pageable); - return TaskResponseMapper.toSliceTaskItemResponse(tasks); - } - - @Override - public TaskBoardResponse getTaskBoardByFilter(Long processorId, LocalDate untilDate, FilterTaskBoardRequest request, Pageable pageable) { - memberService.findActiveMember(processorId); - LocalDateTime untilDateTime = untilDate == null ? LocalDate.now().plusDays(1).atStartOfDay() : untilDate.plusDays(1).atStartOfDay(); - Slice tasks = loadTaskPort.findTaskBoardByFilter(processorId, TaskPolicyConstants.TASK_BOARD_STATUS_FILTER, untilDateTime, request, pageable); - return TaskResponseMapper.toSliceTaskItemResponse(tasks); + LocalDateTime fromDateTime = fromDate != null ? fromDate.atStartOfDay() : null; + List tasks = loadTaskPort.findTaskBoardByFilter(processorId, TaskPolicyConstants.TASK_BOARD_STATUS_FILTER, fromDateTime, request); + return TaskResponseMapper.toTaskBoardResponse(tasks); } }