Skip to content
Merged
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
@@ -0,0 +1,21 @@
package clap.server.adapter.inbound.web.dto.task.request;

import io.swagger.v3.oas.annotations.media.Schema;

public record FilterTaskBoardRequest(
@Schema(description = "라벨 ID")
Long labelId,

@Schema(description = "1차 카테고리 ID, ** 2차 카테고리로 검색할 시에는 1차 카테고리 값은 넣지 않습니다.")
Long mainCategoryId,

@Schema(description = "2차 카테고리 ID")
Long subCategoryId,

@Schema(description = "작업 제목", example = "작업 제목")
String title,

@Schema(description = "요청자 닉네임", example = "atom.park")
String requesterNickname
) {
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
public record TaskItemResponse(
Long taskId,
String taskCode,
String title,
String mainCategoryName,
String categoryName,
String requesterNickname,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,17 +1,20 @@
package clap.server.adapter.inbound.web.task;

import clap.server.adapter.inbound.security.SecurityUserDetails;
import clap.server.adapter.inbound.web.dto.task.request.FilterTaskBoardRequest;
import clap.server.adapter.inbound.web.dto.task.request.UpdateTaskOrderRequest;
import clap.server.adapter.inbound.web.dto.task.response.TaskBoardResponse;
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.TaskBoardUsecase;
import clap.server.application.port.inbound.task.GetTaskBoardUsecase;
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;
Expand All @@ -21,24 +24,30 @@

import java.time.LocalDate;

@Tag(name = "02. Task [담당자]", description = " 작업 보드 API")
@Slf4j
@Tag(name = "02. Task [담당자]")
@WebAdapter
@RestController
@RequiredArgsConstructor
@RequestMapping("/api/task-board")
public class TaskBoardController {
private final TaskBoardUsecase taskBoardUsecase;
private final GetTaskBoardUsecase getTaskBoardUsecase;
private final FilterTaskBoardUsecase filterTaskBoardUsecase;
private final UpdateTaskBoardUsecase updateTaskBoardUsecase;

@Operation(summary = "작업 보드 조회 API")
@GetMapping
@PostMapping
public ResponseEntity<TaskBoardResponse> getTaskBoard(@RequestParam(defaultValue = "0") int page,
@RequestParam(defaultValue = "20") int pageSize,
@Parameter(description = "yyyy-mm-dd 형식으로 입력합니다.") @RequestParam(required = false)
@Parameter(description = "완료 일자 조회 기준, yyyy-mm-dd 형식으로 입력합니다.") @RequestParam(required = false)
@DateTimeFormat(pattern = "yyyy-MM-dd") LocalDate untilDate,
@io.swagger.v3.oas.annotations.parameters.RequestBody(description = "필터링 조회 request") @RequestBody(required = false) FilterTaskBoardRequest request,
@AuthenticationPrincipal SecurityUserDetails userInfo) {
Pageable pageable = PageRequest.of(page, pageSize);
return ResponseEntity.ok(taskBoardUsecase.getTaskBoards(userInfo.getUserId(), untilDate, pageable));
if (request != null) {
return ResponseEntity.ok(filterTaskBoardUsecase.getTaskBoardByFilter(userInfo.getUserId(), untilDate, request, pageable));
} else
return ResponseEntity.ok(getTaskBoardUsecase.getTaskBoards(userInfo.getUserId(), untilDate, pageable));
}

@Operation(summary = "작업 보드 순서 및 상태 변경 API")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import clap.server.adapter.inbound.web.dto.task.FilterPendingApprovalResponse;
import clap.server.adapter.inbound.web.dto.task.FilterRequestedTasksResponse;
import clap.server.adapter.inbound.web.dto.task.FilterTaskListRequest;
import clap.server.adapter.inbound.web.dto.task.request.FilterTaskBoardRequest;
import clap.server.adapter.inbound.web.dto.task.*;
import clap.server.adapter.outbound.persistense.entity.task.TaskEntity;
import clap.server.adapter.outbound.persistense.entity.task.constant.TaskStatus;
Expand All @@ -19,6 +20,7 @@
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 All @@ -27,7 +29,7 @@
@Slf4j
@PersistenceAdapter
@RequiredArgsConstructor
public class TaskPersistenceAdapter implements CommandTaskPort , LoadTaskPort {
public class TaskPersistenceAdapter implements CommandTaskPort, LoadTaskPort {
private final TaskRepository taskRepository;
private final TaskPersistenceMapper taskPersistenceMapper;

Expand Down Expand Up @@ -84,21 +86,37 @@ public List<Task> findYesterdayTaskByDate(LocalDateTime now) {
}

@Override
public Page<FilterAllTasksResponse> findAllTasks (Pageable pageable, FilterTaskListRequest filterTaskListRequest) {
public Page<FilterAllTasksResponse> findAllTasks(Pageable pageable, FilterTaskListRequest filterTaskListRequest) {
Page<Task> taskList = taskRepository.findAllTasks(pageable, filterTaskListRequest)
.map(taskPersistenceMapper::toDomain);
return taskList.map(TaskMapper::toFilterAllTasksResponse);
}

@Override
public Optional<Task> findPrevOrderTaskByProcessorIdAndStatus(Long processorId, TaskStatus taskStatus, Long processorOrder){
public Optional<Task> findPrevOrderTaskByProcessorIdAndStatus(Long processorId, TaskStatus taskStatus, Long processorOrder) {
Optional<TaskEntity> taskEntity = taskRepository.findTopByProcessor_MemberIdAndTaskStatusAndProcessorOrderLessThanOrderByProcessorOrderDesc(processorId, taskStatus, processorOrder);
return taskEntity.map(taskPersistenceMapper::toDomain);
}

@Override
public Optional<Task> findNextOrderTaskByProcessorIdAndStatus(Long processorId, TaskStatus taskStatus, Long processorOrder){
Optional<TaskEntity> taskEntity = taskRepository.findTopByProcessor_MemberIdAndTaskStatusAndProcessorOrderAfterOrderByProcessorOrderDesc(processorId, taskStatus, processorOrder);
public Optional<Task> findNextOrderTaskByProcessorIdAndStatus(Long processorId, TaskStatus taskStatus, Long processorOrder) {
Optional<TaskEntity> taskEntity = taskRepository.findTopByProcessor_MemberIdAndTaskStatusAndProcessorOrderAfterOrderByProcessorOrderDesc(processorId, taskStatus, processorOrder);
return taskEntity.map(taskPersistenceMapper::toDomain);
}

@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)
.stream()
.map(taskPersistenceMapper::toDomain)
.toList());

boolean hasNext = taskList.size() > pageable.getPageSize();
if (hasNext) {
taskList.remove(taskList.size() - 1);
}
return new SliceImpl<>(taskList, pageable, hasNext);
}


}
Original file line number Diff line number Diff line change
@@ -1,14 +1,21 @@
package clap.server.adapter.outbound.persistense.repository.task;

import clap.server.adapter.inbound.web.dto.task.FilterTaskListRequest;
import clap.server.adapter.inbound.web.dto.task.request.FilterTaskBoardRequest;
import clap.server.adapter.outbound.persistense.entity.task.TaskEntity;
import clap.server.adapter.outbound.persistense.entity.task.constant.TaskStatus;
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;

public interface TaskCustomRepository {

Page<TaskEntity> findTasksRequestedByUser(Long requesterId, Pageable pageable, FilterTaskListRequest findTaskListRequest);
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);
Page<TaskEntity> findTasksAssignedByManager(Long processorId, Pageable pageable, FilterTaskListRequest findTaskListRequest);
}
Original file line number Diff line number Diff line change
@@ -1,16 +1,16 @@
package clap.server.adapter.outbound.persistense.repository.task;

import clap.server.adapter.inbound.web.dto.task.FilterTaskListRequest;
import clap.server.adapter.inbound.web.dto.task.request.FilterTaskBoardRequest;
import clap.server.adapter.outbound.persistense.entity.task.TaskEntity;
import clap.server.adapter.outbound.persistense.entity.task.constant.TaskStatus;
import com.querydsl.core.BooleanBuilder;
import com.querydsl.core.types.OrderSpecifier;
import com.querydsl.core.types.dsl.DateTimePath;
import com.querydsl.jpa.impl.JPAQueryFactory;
import lombok.RequiredArgsConstructor;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageImpl;
import org.springframework.data.domain.Pageable;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.domain.*;
import org.springframework.stereotype.Repository;

import java.time.LocalDateTime;
Expand All @@ -19,6 +19,7 @@
import static clap.server.adapter.outbound.persistense.entity.task.QTaskEntity.taskEntity;
import static com.querydsl.core.types.Order.*;

@Slf4j
@Repository
@RequiredArgsConstructor
public class TaskCustomRepositoryImpl implements TaskCustomRepository {
Expand Down Expand Up @@ -69,6 +70,47 @@ public Page<TaskEntity> findAllTasks(Pageable pageable, FilterTaskListRequest fi
return getTasksPage(pageable, whereClause, filterTaskListRequest.orderRequest().sortBy(), filterTaskListRequest.orderRequest().sortDirection());
}

@Override
public List<TaskEntity> findTasksByFilter(Long processorId, List<TaskStatus> statuses, LocalDateTime untilDateTime, FilterTaskBoardRequest request, Pageable pageable) {
BooleanBuilder whereClause = createTaskBoardFilter(processorId, statuses, untilDateTime, request);
return queryFactory
.selectFrom(taskEntity)
.where(whereClause)
.orderBy(taskEntity.processorOrder.asc())
.limit(pageable.getPageSize() + 1)
.offset(pageable.getOffset())
.fetch();
}

private BooleanBuilder createTaskBoardFilter(Long processorId, List<TaskStatus> statuses, LocalDateTime untilDateTime, FilterTaskBoardRequest request) {
BooleanBuilder whereClause = new BooleanBuilder();

whereClause.and(taskEntity.processor.memberId.eq(processorId));
whereClause.and(taskEntity.taskStatus.in(statuses));
whereClause.and(taskEntity.finishedAt.isNull().or(taskEntity.finishedAt.loe(untilDateTime)));

if (request.labelId() != null) {
whereClause.and(taskEntity.label.labelId.eq(request.labelId()));
}
if (request.mainCategoryId() != null) {
whereClause.and(taskEntity.category.mainCategory.categoryId.eq(request.mainCategoryId()));
}
if (request.subCategoryId() != null) {
whereClause.and(taskEntity.category.categoryId.eq(request.subCategoryId()));
}
if (request.title() != null && !request.title().isEmpty()) {
String titleFilter = "%" + request.title() + "%";
whereClause.and(taskEntity.title.like(titleFilter));
}
if (request.requesterNickname() != null && !request.requesterNickname().isEmpty()) {
String nicknameFilter = "%" + request.requesterNickname().toLowerCase() + "%";
whereClause.and(taskEntity.requester.nickname.lower().like(nicknameFilter));

}

return whereClause;
}

private BooleanBuilder createFilter(FilterTaskListRequest request) {
BooleanBuilder whereClause = new BooleanBuilder();
if (request.term() != null) {
Expand All @@ -90,6 +132,7 @@ private BooleanBuilder createFilter(FilterTaskListRequest request) {
return whereClause;
}


private Page<TaskEntity> getTasksPage(Pageable pageable, BooleanBuilder whereClause, String sortBy, String sortDirection) {
OrderSpecifier<?> orderSpecifier = getOrderSpecifier(sortBy, sortDirection);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,7 @@ private static TaskItemResponse toTaskItemResponse(Task task) {
return new TaskItemResponse(
task.getTaskId(),
task.getTaskCode(),
task.getTitle(),
task.getCategory().getMainCategory().getName(),
task.getCategory().getName(),
task.getRequester().getNickname(),
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package clap.server.application.port.inbound.task;

import clap.server.adapter.inbound.web.dto.task.request.FilterTaskBoardRequest;
import clap.server.adapter.inbound.web.dto.task.response.TaskBoardResponse;
import org.springframework.data.domain.Pageable;

import java.time.LocalDate;

public interface FilterTaskBoardUsecase {
TaskBoardResponse getTaskBoardByFilter(Long processorId, LocalDate untilDate, FilterTaskBoardRequest request, Pageable pageable);
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,6 @@

import java.time.LocalDate;

public interface TaskBoardUsecase {
TaskBoardResponse getTaskBoards(Long processorId, LocalDate untilDate, Pageable pageable);
public interface GetTaskBoardUsecase {
TaskBoardResponse getTaskBoards(Long processorId, LocalDate untilDate,Pageable pageable);
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import clap.server.adapter.inbound.web.dto.task.FilterPendingApprovalResponse;
import clap.server.adapter.inbound.web.dto.task.FilterRequestedTasksResponse;
import clap.server.adapter.inbound.web.dto.task.FilterTaskListRequest;
import clap.server.adapter.inbound.web.dto.task.request.FilterTaskBoardRequest;
import clap.server.adapter.outbound.persistense.entity.task.constant.TaskStatus;
import clap.server.domain.model.task.Task;
import org.springframework.data.domain.Page;
Expand Down Expand Up @@ -35,4 +36,6 @@ public interface LoadTaskPort {
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);
}
Original file line number Diff line number Diff line change
@@ -1,14 +1,17 @@
package clap.server.application.service.task;

import clap.server.adapter.inbound.web.dto.task.request.FilterTaskBoardRequest;
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.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 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;
Expand All @@ -17,20 +20,29 @@
import java.time.LocalDateTime;
import java.util.List;

@Slf4j
@ApplicationService
@RequiredArgsConstructor
class TaskBoardService implements TaskBoardUsecase {
@Transactional(readOnly = true)
class GetTaskBoardService implements GetTaskBoardUsecase, FilterTaskBoardUsecase {
private final static List<TaskStatus> VIEWABLE_STATUSES = TaskStatus.getTaskBoardStatusList();

private final MemberService memberService;
private final LoadTaskPort loadTaskPort;

@Override
@Transactional(readOnly = true)
public TaskBoardResponse getTaskBoards(Long processorId, LocalDate untilDate, Pageable pageable) {
memberService.findById(processorId);
memberService.findActiveMember(processorId);
LocalDateTime untilDateTime = untilDate == null ? LocalDate.now().plusDays(1).atStartOfDay() : untilDate.plusDays(1).atStartOfDay();
Slice<Task> tasks = loadTaskPort.findByProcessorAndStatus(processorId, VIEWABLE_STATUSES, untilDateTime, pageable);
return TaskMapper.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, VIEWABLE_STATUSES, untilDateTime, request, pageable);
return TaskMapper.toSliceTaskItemResponse(tasks);
}
}