Skip to content

Commit 8b08696

Browse files
authored
Merge pull request #184 from TaskFlow-CLAP/CLAP-174
CLAP-174 작업 보드 조회 API에 필터링 적용
2 parents 5d2896f + 7ab46ef commit 8b08696

File tree

11 files changed

+146
-20
lines changed

11 files changed

+146
-20
lines changed
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
package clap.server.adapter.inbound.web.dto.task.request;
2+
3+
import io.swagger.v3.oas.annotations.media.Schema;
4+
5+
public record FilterTaskBoardRequest(
6+
@Schema(description = "라벨 ID")
7+
Long labelId,
8+
9+
@Schema(description = "1차 카테고리 ID, ** 2차 카테고리로 검색할 시에는 1차 카테고리 값은 넣지 않습니다.")
10+
Long mainCategoryId,
11+
12+
@Schema(description = "2차 카테고리 ID")
13+
Long subCategoryId,
14+
15+
@Schema(description = "작업 제목", example = "작업 제목")
16+
String title,
17+
18+
@Schema(description = "요청자 닉네임", example = "atom.park")
19+
String requesterNickname
20+
) {
21+
}

src/main/java/clap/server/adapter/inbound/web/dto/task/response/TaskItemResponse.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
public record TaskItemResponse(
88
Long taskId,
99
String taskCode,
10+
String title,
1011
String mainCategoryName,
1112
String categoryName,
1213
String requesterNickname,

src/main/java/clap/server/adapter/inbound/web/task/TaskBoardController.java

Lines changed: 15 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,20 @@
11
package clap.server.adapter.inbound.web.task;
22

33
import clap.server.adapter.inbound.security.SecurityUserDetails;
4+
import clap.server.adapter.inbound.web.dto.task.request.FilterTaskBoardRequest;
45
import clap.server.adapter.inbound.web.dto.task.request.UpdateTaskOrderRequest;
56
import clap.server.adapter.inbound.web.dto.task.response.TaskBoardResponse;
67
import clap.server.adapter.outbound.persistense.entity.task.constant.TaskStatus;
8+
import clap.server.application.port.inbound.task.FilterTaskBoardUsecase;
79
import clap.server.application.port.inbound.task.UpdateTaskBoardUsecase;
8-
import clap.server.application.port.inbound.task.TaskBoardUsecase;
10+
import clap.server.application.port.inbound.task.GetTaskBoardUsecase;
911
import clap.server.common.annotation.architecture.WebAdapter;
1012
import io.swagger.v3.oas.annotations.Operation;
1113
import io.swagger.v3.oas.annotations.Parameter;
1214
import io.swagger.v3.oas.annotations.media.Schema;
1315
import io.swagger.v3.oas.annotations.tags.Tag;
1416
import lombok.RequiredArgsConstructor;
17+
import lombok.extern.slf4j.Slf4j;
1518
import org.springframework.data.domain.PageRequest;
1619
import org.springframework.data.domain.Pageable;
1720
import org.springframework.format.annotation.DateTimeFormat;
@@ -21,24 +24,30 @@
2124

2225
import java.time.LocalDate;
2326

24-
@Tag(name = "02. Task [담당자]", description = " 작업 보드 API")
27+
@Slf4j
28+
@Tag(name = "02. Task [담당자]")
2529
@WebAdapter
2630
@RestController
2731
@RequiredArgsConstructor
2832
@RequestMapping("/api/task-board")
2933
public class TaskBoardController {
30-
private final TaskBoardUsecase taskBoardUsecase;
34+
private final GetTaskBoardUsecase getTaskBoardUsecase;
35+
private final FilterTaskBoardUsecase filterTaskBoardUsecase;
3136
private final UpdateTaskBoardUsecase updateTaskBoardUsecase;
3237

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

4453
@Operation(summary = "작업 보드 순서 및 상태 변경 API")

src/main/java/clap/server/adapter/outbound/persistense/TaskPersistenceAdapter.java

Lines changed: 23 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
import clap.server.adapter.inbound.web.dto.task.FilterPendingApprovalResponse;
55
import clap.server.adapter.inbound.web.dto.task.FilterRequestedTasksResponse;
66
import clap.server.adapter.inbound.web.dto.task.FilterTaskListRequest;
7+
import clap.server.adapter.inbound.web.dto.task.request.FilterTaskBoardRequest;
78
import clap.server.adapter.inbound.web.dto.task.*;
89
import clap.server.adapter.outbound.persistense.entity.task.TaskEntity;
910
import clap.server.adapter.outbound.persistense.entity.task.constant.TaskStatus;
@@ -19,6 +20,7 @@
1920
import org.springframework.data.domain.Page;
2021
import org.springframework.data.domain.Pageable;
2122
import org.springframework.data.domain.Slice;
23+
import org.springframework.data.domain.SliceImpl;
2224

2325
import java.time.LocalDateTime;
2426
import java.util.List;
@@ -27,7 +29,7 @@
2729
@Slf4j
2830
@PersistenceAdapter
2931
@RequiredArgsConstructor
30-
public class TaskPersistenceAdapter implements CommandTaskPort , LoadTaskPort {
32+
public class TaskPersistenceAdapter implements CommandTaskPort, LoadTaskPort {
3133
private final TaskRepository taskRepository;
3234
private final TaskPersistenceMapper taskPersistenceMapper;
3335

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

8688
@Override
87-
public Page<FilterAllTasksResponse> findAllTasks (Pageable pageable, FilterTaskListRequest filterTaskListRequest) {
89+
public Page<FilterAllTasksResponse> findAllTasks(Pageable pageable, FilterTaskListRequest filterTaskListRequest) {
8890
Page<Task> taskList = taskRepository.findAllTasks(pageable, filterTaskListRequest)
8991
.map(taskPersistenceMapper::toDomain);
9092
return taskList.map(TaskMapper::toFilterAllTasksResponse);
9193
}
94+
9295
@Override
93-
public Optional<Task> findPrevOrderTaskByProcessorIdAndStatus(Long processorId, TaskStatus taskStatus, Long processorOrder){
96+
public Optional<Task> findPrevOrderTaskByProcessorIdAndStatus(Long processorId, TaskStatus taskStatus, Long processorOrder) {
9497
Optional<TaskEntity> taskEntity = taskRepository.findTopByProcessor_MemberIdAndTaskStatusAndProcessorOrderLessThanOrderByProcessorOrderDesc(processorId, taskStatus, processorOrder);
9598
return taskEntity.map(taskPersistenceMapper::toDomain);
9699
}
97100

98101
@Override
99-
public Optional<Task> findNextOrderTaskByProcessorIdAndStatus(Long processorId, TaskStatus taskStatus, Long processorOrder){
100-
Optional<TaskEntity> taskEntity = taskRepository.findTopByProcessor_MemberIdAndTaskStatusAndProcessorOrderAfterOrderByProcessorOrderDesc(processorId, taskStatus, processorOrder);
102+
public Optional<Task> findNextOrderTaskByProcessorIdAndStatus(Long processorId, TaskStatus taskStatus, Long processorOrder) {
103+
Optional<TaskEntity> taskEntity = taskRepository.findTopByProcessor_MemberIdAndTaskStatusAndProcessorOrderAfterOrderByProcessorOrderDesc(processorId, taskStatus, processorOrder);
101104
return taskEntity.map(taskPersistenceMapper::toDomain);
102105
}
103106

107+
@Override
108+
public Slice<Task> findTaskBoardByFilter(Long processorId, List<TaskStatus> statuses, LocalDateTime untilDate, FilterTaskBoardRequest request, Pageable pageable) {
109+
List<Task> taskList = new java.util.ArrayList<>(taskRepository.findTasksByFilter(processorId, statuses, untilDate, request, pageable)
110+
.stream()
111+
.map(taskPersistenceMapper::toDomain)
112+
.toList());
113+
114+
boolean hasNext = taskList.size() > pageable.getPageSize();
115+
if (hasNext) {
116+
taskList.remove(taskList.size() - 1);
117+
}
118+
return new SliceImpl<>(taskList, pageable, hasNext);
119+
}
120+
121+
104122
}
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,21 @@
11
package clap.server.adapter.outbound.persistense.repository.task;
22

33
import clap.server.adapter.inbound.web.dto.task.FilterTaskListRequest;
4+
import clap.server.adapter.inbound.web.dto.task.request.FilterTaskBoardRequest;
45
import clap.server.adapter.outbound.persistense.entity.task.TaskEntity;
6+
import clap.server.adapter.outbound.persistense.entity.task.constant.TaskStatus;
57
import org.springframework.data.domain.Page;
68
import org.springframework.data.domain.Pageable;
9+
import org.springframework.data.domain.Slice;
10+
11+
import java.time.LocalDateTime;
12+
import java.util.List;
713

814
public interface TaskCustomRepository {
915

1016
Page<TaskEntity> findTasksRequestedByUser(Long requesterId, Pageable pageable, FilterTaskListRequest findTaskListRequest);
1117
Page<TaskEntity> findPendingApprovalTasks(Pageable pageable, FilterTaskListRequest findTaskListRequest);
1218
Page<TaskEntity> findAllTasks(Pageable pageable, FilterTaskListRequest findTaskListRequest);
19+
List<TaskEntity> findTasksByFilter(Long processorId, List<TaskStatus> statuses, LocalDateTime localDateTime, FilterTaskBoardRequest request, Pageable pageable);
1320
Page<TaskEntity> findTasksAssignedByManager(Long processorId, Pageable pageable, FilterTaskListRequest findTaskListRequest);
1421
}

src/main/java/clap/server/adapter/outbound/persistense/repository/task/TaskCustomRepositoryImpl.java

Lines changed: 46 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,16 @@
11
package clap.server.adapter.outbound.persistense.repository.task;
22

33
import clap.server.adapter.inbound.web.dto.task.FilterTaskListRequest;
4+
import clap.server.adapter.inbound.web.dto.task.request.FilterTaskBoardRequest;
45
import clap.server.adapter.outbound.persistense.entity.task.TaskEntity;
56
import clap.server.adapter.outbound.persistense.entity.task.constant.TaskStatus;
67
import com.querydsl.core.BooleanBuilder;
78
import com.querydsl.core.types.OrderSpecifier;
89
import com.querydsl.core.types.dsl.DateTimePath;
910
import com.querydsl.jpa.impl.JPAQueryFactory;
1011
import lombok.RequiredArgsConstructor;
11-
import org.springframework.data.domain.Page;
12-
import org.springframework.data.domain.PageImpl;
13-
import org.springframework.data.domain.Pageable;
12+
import lombok.extern.slf4j.Slf4j;
13+
import org.springframework.data.domain.*;
1414
import org.springframework.stereotype.Repository;
1515

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

22+
@Slf4j
2223
@Repository
2324
@RequiredArgsConstructor
2425
public class TaskCustomRepositoryImpl implements TaskCustomRepository {
@@ -69,6 +70,47 @@ public Page<TaskEntity> findAllTasks(Pageable pageable, FilterTaskListRequest fi
6970
return getTasksPage(pageable, whereClause, filterTaskListRequest.orderRequest().sortBy(), filterTaskListRequest.orderRequest().sortDirection());
7071
}
7172

73+
@Override
74+
public List<TaskEntity> findTasksByFilter(Long processorId, List<TaskStatus> statuses, LocalDateTime untilDateTime, FilterTaskBoardRequest request, Pageable pageable) {
75+
BooleanBuilder whereClause = createTaskBoardFilter(processorId, statuses, untilDateTime, request);
76+
return queryFactory
77+
.selectFrom(taskEntity)
78+
.where(whereClause)
79+
.orderBy(taskEntity.processorOrder.asc())
80+
.limit(pageable.getPageSize() + 1)
81+
.offset(pageable.getOffset())
82+
.fetch();
83+
}
84+
85+
private BooleanBuilder createTaskBoardFilter(Long processorId, List<TaskStatus> statuses, LocalDateTime untilDateTime, FilterTaskBoardRequest request) {
86+
BooleanBuilder whereClause = new BooleanBuilder();
87+
88+
whereClause.and(taskEntity.processor.memberId.eq(processorId));
89+
whereClause.and(taskEntity.taskStatus.in(statuses));
90+
whereClause.and(taskEntity.finishedAt.isNull().or(taskEntity.finishedAt.loe(untilDateTime)));
91+
92+
if (request.labelId() != null) {
93+
whereClause.and(taskEntity.label.labelId.eq(request.labelId()));
94+
}
95+
if (request.mainCategoryId() != null) {
96+
whereClause.and(taskEntity.category.mainCategory.categoryId.eq(request.mainCategoryId()));
97+
}
98+
if (request.subCategoryId() != null) {
99+
whereClause.and(taskEntity.category.categoryId.eq(request.subCategoryId()));
100+
}
101+
if (request.title() != null && !request.title().isEmpty()) {
102+
String titleFilter = "%" + request.title() + "%";
103+
whereClause.and(taskEntity.title.like(titleFilter));
104+
}
105+
if (request.requesterNickname() != null && !request.requesterNickname().isEmpty()) {
106+
String nicknameFilter = "%" + request.requesterNickname().toLowerCase() + "%";
107+
whereClause.and(taskEntity.requester.nickname.lower().like(nicknameFilter));
108+
109+
}
110+
111+
return whereClause;
112+
}
113+
72114
private BooleanBuilder createFilter(FilterTaskListRequest request) {
73115
BooleanBuilder whereClause = new BooleanBuilder();
74116
if (request.term() != null) {
@@ -90,6 +132,7 @@ private BooleanBuilder createFilter(FilterTaskListRequest request) {
90132
return whereClause;
91133
}
92134

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

src/main/java/clap/server/application/mapper/TaskMapper.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -132,6 +132,7 @@ private static TaskItemResponse toTaskItemResponse(Task task) {
132132
return new TaskItemResponse(
133133
task.getTaskId(),
134134
task.getTaskCode(),
135+
task.getTitle(),
135136
task.getCategory().getMainCategory().getName(),
136137
task.getCategory().getName(),
137138
task.getRequester().getNickname(),
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
package clap.server.application.port.inbound.task;
2+
3+
import clap.server.adapter.inbound.web.dto.task.request.FilterTaskBoardRequest;
4+
import clap.server.adapter.inbound.web.dto.task.response.TaskBoardResponse;
5+
import org.springframework.data.domain.Pageable;
6+
7+
import java.time.LocalDate;
8+
9+
public interface FilterTaskBoardUsecase {
10+
TaskBoardResponse getTaskBoardByFilter(Long processorId, LocalDate untilDate, FilterTaskBoardRequest request, Pageable pageable);
11+
}

src/main/java/clap/server/application/port/inbound/task/TaskBoardUsecase.java renamed to src/main/java/clap/server/application/port/inbound/task/GetTaskBoardUsecase.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,6 @@
55

66
import java.time.LocalDate;
77

8-
public interface TaskBoardUsecase {
9-
TaskBoardResponse getTaskBoards(Long processorId, LocalDate untilDate, Pageable pageable);
8+
public interface GetTaskBoardUsecase {
9+
TaskBoardResponse getTaskBoards(Long processorId, LocalDate untilDate,Pageable pageable);
1010
}

src/main/java/clap/server/application/port/outbound/task/LoadTaskPort.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
import clap.server.adapter.inbound.web.dto.task.FilterPendingApprovalResponse;
66
import clap.server.adapter.inbound.web.dto.task.FilterRequestedTasksResponse;
77
import clap.server.adapter.inbound.web.dto.task.FilterTaskListRequest;
8+
import clap.server.adapter.inbound.web.dto.task.request.FilterTaskBoardRequest;
89
import clap.server.adapter.outbound.persistense.entity.task.constant.TaskStatus;
910
import clap.server.domain.model.task.Task;
1011
import org.springframework.data.domain.Page;
@@ -35,4 +36,6 @@ public interface LoadTaskPort {
3536
Optional<Task> findPrevOrderTaskByProcessorIdAndStatus(Long processorId, TaskStatus taskStatus, Long processorOrder);
3637

3738
Optional<Task> findNextOrderTaskByProcessorIdAndStatus(Long processorId, TaskStatus taskStatus, Long processorOrder);
39+
40+
Slice<Task> findTaskBoardByFilter(Long processorId, List<TaskStatus> statuses, LocalDateTime untilDateTime, FilterTaskBoardRequest request, Pageable pageable);
3841
}

0 commit comments

Comments
 (0)