Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -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<Long> mainCategoryIds,

@Schema(description = "2차 카테고리 ID")
Long subCategoryId,
@Schema(description = "2차 카테고리 ID 목록", example = "[2, 4]")
@NotNull
List<Long> categoryIds,

@Schema(description = "작업 제목", example = "작업 제목")
String title,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,6 @@
public record TaskBoardResponse(
List<TaskItemResponse> tasksInProgress,
List<TaskItemResponse> tasksPendingComplete,
List<TaskItemResponse> tasksCompleted,
boolean hasNext,
boolean isFirst,
boolean isLast
List<TaskItemResponse> tasksCompleted
){
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,17 +7,13 @@
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;
import io.swagger.v3.oas.annotations.Parameter;
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;
Expand All @@ -26,38 +22,32 @@

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;

@Operation(summary = "작업 보드 조회 API")
@Secured({"ROLE_MANAGER"})
@GetMapping
public ResponseEntity<TaskBoardResponse> 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<TaskBoardResponse> 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) {
Expand Down
Original file line number Diff line number Diff line change
@@ -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;
Expand All @@ -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;
Expand Down Expand Up @@ -62,9 +60,9 @@ public Page<Task> findPendingApprovalTasks(Pageable pageable, FilterTaskListRequ
}

@Override
public Slice<Task> findByProcessorAndStatus(Long processorId, List<TaskStatus> statuses, LocalDateTime untilDate, Pageable pageable) {
Slice<TaskEntity> tasks = taskRepository.findTasksWithTaskStatusAndCompletedAt(processorId, statuses, untilDate, pageable);
return tasks.map(taskPersistenceMapper::toDomain);
public List<Task> findByProcessorAndStatus(Long processorId, List<TaskStatus> statuses, LocalDateTime fromDateTime) {
List<TaskEntity> tasks = taskRepository.findTasksWithTaskStatusAndCompletedAt(processorId, statuses, fromDateTime);
return tasks.stream().map(taskPersistenceMapper::toDomain).toList();
}

@Override
Expand Down Expand Up @@ -98,17 +96,10 @@ public Optional<Task> findNextOrderTaskByProcessorIdAndStatus(Long processorId,
}

@Override
public Slice<Task> findTaskBoardByFilter(Long processorId, List<TaskStatus> statuses, LocalDateTime untilDate, FilterTaskBoardRequest request, Pageable pageable) {
List<Task> taskList = new java.util.ArrayList<>(taskRepository.findTasksByFilter(processorId, statuses, untilDate, request, pageable)
public List<Task> findTaskBoardByFilter(Long processorId, List<TaskStatus> 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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,6 @@ public interface TaskCustomRepository {
List<TeamMemberTaskResponse> findTeamStatus(Long memberId, FilterTeamStatusRequest filter);
Page<TaskEntity> findPendingApprovalTasks(Pageable pageable, FilterTaskListRequest findTaskListRequest);
Page<TaskEntity> findAllTasks(Pageable pageable, FilterTaskListRequest findTaskListRequest);
List<TaskEntity> findTasksByFilter(Long processorId, List<TaskStatus> statuses, LocalDateTime localDateTime, FilterTaskBoardRequest request, Pageable pageable);
List<TaskEntity> findTasksByFilter(Long processorId, List<TaskStatus> statuses, LocalDateTime localDateTime, FilterTaskBoardRequest request);
Page<TaskEntity> findTasksAssignedByManager(Long processorId, Pageable pageable, FilterTaskListRequest findTaskListRequest);
}
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,6 @@
public class TaskCustomRepositoryImpl implements TaskCustomRepository {

private final JPAQueryFactory queryFactory;
private final EntityManager entityManager;

@Override
public Page<TaskEntity> findTasksRequestedByUser(Long requesterId, Pageable pageable, FilterTaskListRequest filterTaskListRequest) {
Expand All @@ -58,7 +57,6 @@ public Page<TaskEntity> findTasksAssignedByManager(Long processorId, Pageable pa

@Override
public List<TeamMemberTaskResponse> findTeamStatus(Long memberId, FilterTeamStatusRequest filter) {
// 1. 담당자 목록을 가져옴 (페이징 제거)
List<Long> processorIds = queryFactory
.select(taskEntity.processor.memberId)
.from(taskEntity)
Expand Down Expand Up @@ -176,32 +174,37 @@ public Page<TaskEntity> findAllTasks(Pageable pageable, FilterTaskListRequest fi
}

@Override
public List<TaskEntity> findTasksByFilter(Long processorId, List<TaskStatus> statuses, LocalDateTime untilDateTime, FilterTaskBoardRequest request, Pageable pageable) {
BooleanBuilder builder = createTaskBoardFilter(processorId, statuses, untilDateTime, request);
public List<TaskEntity> findTasksByFilter(Long processorId, List<TaskStatus> 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<TaskStatus> statuses, LocalDateTime untilDateTime, FilterTaskBoardRequest request) {
private BooleanBuilder createTaskBoardFilter(Long processorId, List<TaskStatus> 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() + "%";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,15 +38,16 @@ List<TaskEntity> 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<TaskEntity> findTasksWithTaskStatusAndCompletedAt(
List<TaskEntity> findTasksWithTaskStatusAndCompletedAt(
@Param("processorId") Long processorId,
@Param("taskStatus") List<TaskStatus> taskStatus,
@Param("untilDate") LocalDateTime untilDate,
Pageable pageable
@Param("fromDateTime") LocalDateTime fromDateTime
);


Optional<TaskEntity> findByTaskIdAndTaskStatus(Long id, TaskStatus status);

Optional<TaskEntity> findTopByProcessor_MemberIdAndTaskStatusAndProcessorOrderLessThanOrderByProcessorOrderDesc(Long processorId, TaskStatus taskStatus, Long processorOrder);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -119,18 +119,15 @@ public static FilterAllTasksResponse toFilterAllTasksResponse(Task task) {
);
}

public static TaskBoardResponse toSliceTaskItemResponse(Slice<Task> tasks) {
Map<TaskStatus, List<TaskItemResponse>> tasksByStatus =tasks.getContent().stream()
public static TaskBoardResponse toTaskBoardResponse(List<Task> tasks) {
Map<TaskStatus, List<TaskItemResponse>> 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())
);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}

This file was deleted.

Original file line number Diff line number Diff line change
@@ -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;
Expand All @@ -27,15 +26,15 @@ public interface LoadTaskPort {

Page<Task> findAllTasks(Pageable pageable, FilterTaskListRequest findTaskListRequest);

Slice<Task> findByProcessorAndStatus(Long processorId, List<TaskStatus> statuses, LocalDateTime untilDate, Pageable pageable);
List<Task> findByProcessorAndStatus(Long processorId, List<TaskStatus> statuses, LocalDateTime fromDate);

Optional<Task> findByIdAndStatus(Long id, TaskStatus status);

Optional<Task> findPrevOrderTaskByProcessorIdAndStatus(Long processorId, TaskStatus taskStatus, Long processorOrder);

Optional<Task> findNextOrderTaskByProcessorIdAndStatus(Long processorId, TaskStatus taskStatus, Long processorOrder);

Slice<Task> findTaskBoardByFilter(Long processorId, List<TaskStatus> statuses, LocalDateTime untilDateTime, FilterTaskBoardRequest request, Pageable pageable);
List<Task> findTaskBoardByFilter(Long processorId, List<TaskStatus> statuses, LocalDateTime untilDateTime, FilterTaskBoardRequest request);

List<TeamMemberTaskResponse> findTeamStatus(Long memberId, FilterTeamStatusRequest filter);
}
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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<Task> 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<Task> tasks = loadTaskPort.findTaskBoardByFilter(processorId, TaskPolicyConstants.TASK_BOARD_STATUS_FILTER, untilDateTime, request, pageable);
return TaskResponseMapper.toSliceTaskItemResponse(tasks);
LocalDateTime fromDateTime = fromDate != null ? fromDate.atStartOfDay() : null;
List<Task> tasks = loadTaskPort.findTaskBoardByFilter(processorId, TaskPolicyConstants.TASK_BOARD_STATUS_FILTER, fromDateTime, request);
return TaskResponseMapper.toTaskBoardResponse(tasks);
}
}
Loading