From d6071d7835678ee7a6de3cd3f6b64e7a28a0bbe7 Mon Sep 17 00:00:00 2001 From: nano-mm Date: Sun, 2 Feb 2025 06:41:24 +0900 Subject: [PATCH 1/4] =?UTF-8?q?CLAP-72=20feat:=20=ED=8C=80=20=ED=98=84?= =?UTF-8?q?=ED=99=A9=20=EC=A1=B0=ED=9A=8C=20API=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../task/request/FilterTeamStatusRequest.java | 25 +++++ .../task/response/TeamMemberTaskResponse.java | 18 +++ .../dto/task/response/TeamStatusResponse.java | 20 ++++ .../web/task/TeamStatusController.java | 61 +++++++++++ .../persistense/TaskPersistenceAdapter.java | 1 + .../mapper/TaskPersistenceMapper.java | 1 - .../repository/task/TaskCustomRepository.java | 4 +- .../task/TaskCustomRepositoryImpl.java | 103 +++++++++++++++++- .../repository/task/TaskRepository.java | 1 + .../server/application/mapper/TaskMapper.java | 4 +- .../inbound/task/FilterTeamStatusUsecase.java | 10 ++ .../inbound/task/LoadTeamStatusUsecase.java | 10 ++ .../service/task/TeamStatusService.java | 36 ++++++ .../clap/server/domain/model/task/Task.java | 2 + 14 files changed, 292 insertions(+), 4 deletions(-) create mode 100644 src/main/java/clap/server/adapter/inbound/web/dto/task/request/FilterTeamStatusRequest.java create mode 100644 src/main/java/clap/server/adapter/inbound/web/dto/task/response/TeamMemberTaskResponse.java create mode 100644 src/main/java/clap/server/adapter/inbound/web/dto/task/response/TeamStatusResponse.java create mode 100644 src/main/java/clap/server/adapter/inbound/web/task/TeamStatusController.java create mode 100644 src/main/java/clap/server/application/port/inbound/task/FilterTeamStatusUsecase.java create mode 100644 src/main/java/clap/server/application/port/inbound/task/LoadTeamStatusUsecase.java create mode 100644 src/main/java/clap/server/application/service/task/TeamStatusService.java diff --git a/src/main/java/clap/server/adapter/inbound/web/dto/task/request/FilterTeamStatusRequest.java b/src/main/java/clap/server/adapter/inbound/web/dto/task/request/FilterTeamStatusRequest.java new file mode 100644 index 00000000..43ddbcb4 --- /dev/null +++ b/src/main/java/clap/server/adapter/inbound/web/dto/task/request/FilterTeamStatusRequest.java @@ -0,0 +1,25 @@ +package clap.server.adapter.inbound.web.dto.task.request; + +import io.swagger.v3.oas.annotations.media.Schema; + +import java.util.List; + +public record FilterTeamStatusRequest( + @Schema(description = "정렬 기준 (기여도순, 기본)", example = "기여도순") + String sortBy, + @Schema(description = "1차 카테고리 ID 목록", example = "[10, 20, 30]") + List mainCategoryIds, + @Schema(description = "2차 카테고리 ID 목록", example = "[1, 2, 3]") + List categoryIds, + @Schema(description = "작업 타이틀 검색", example = "타이틀1") + String taskTitle +) { + public FilterTeamStatusRequest { + sortBy = (sortBy == null || sortBy.isEmpty()) ? "기본" : sortBy; + mainCategoryIds = mainCategoryIds == null ? List.of() : mainCategoryIds; + categoryIds = categoryIds == null ? List.of() : categoryIds; + taskTitle = taskTitle == null ? "" : taskTitle; + } +} + + diff --git a/src/main/java/clap/server/adapter/inbound/web/dto/task/response/TeamMemberTaskResponse.java b/src/main/java/clap/server/adapter/inbound/web/dto/task/response/TeamMemberTaskResponse.java new file mode 100644 index 00000000..e01ce75f --- /dev/null +++ b/src/main/java/clap/server/adapter/inbound/web/dto/task/response/TeamMemberTaskResponse.java @@ -0,0 +1,18 @@ +package clap.server.adapter.inbound.web.dto.task.response; + +import java.util.List; + +public record TeamMemberTaskResponse( + Long processorId, + String nickname, + String imageUrl, + String department, + int inProgressTaskCount, + int pendingTaskCount, + int totalTaskCount, + List tasks +) { + public TeamMemberTaskResponse { + tasks = (tasks == null) ? List.of() : tasks; + } +} diff --git a/src/main/java/clap/server/adapter/inbound/web/dto/task/response/TeamStatusResponse.java b/src/main/java/clap/server/adapter/inbound/web/dto/task/response/TeamStatusResponse.java new file mode 100644 index 00000000..59012222 --- /dev/null +++ b/src/main/java/clap/server/adapter/inbound/web/dto/task/response/TeamStatusResponse.java @@ -0,0 +1,20 @@ +package clap.server.adapter.inbound.web.dto.task.response; + +import java.util.List; + +public record TeamStatusResponse( + List members, + boolean hasNext, + boolean isFirst, + boolean isLast +) { + public TeamStatusResponse(List members, int pageNumber, int pageSize) { + this( + (members == null) ? List.of() : members, + (members != null && members.size() > pageSize), + pageNumber == 0, + (members == null || members.size() <= pageSize) + ); + } +} + diff --git a/src/main/java/clap/server/adapter/inbound/web/task/TeamStatusController.java b/src/main/java/clap/server/adapter/inbound/web/task/TeamStatusController.java new file mode 100644 index 00000000..ac1607e6 --- /dev/null +++ b/src/main/java/clap/server/adapter/inbound/web/task/TeamStatusController.java @@ -0,0 +1,61 @@ +package clap.server.adapter.inbound.web.task; + +import clap.server.adapter.inbound.web.dto.task.request.FilterTeamStatusRequest; + +import clap.server.adapter.inbound.web.dto.task.response.TeamStatusResponse; +import clap.server.application.port.inbound.task.FilterTeamStatusUsecase; +import clap.server.application.port.inbound.task.LoadTeamStatusUsecase; +import clap.server.common.annotation.architecture.WebAdapter; +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.http.ResponseEntity; +import org.springframework.web.bind.annotation.*; + +import java.util.List; + +@RestController +@RequestMapping("/api/team-status") +@RequiredArgsConstructor +@Tag(name = "팀 현황 조회 API") +@WebAdapter +public class TeamStatusController { + + private final LoadTeamStatusUsecase loadTeamStatusUsecase; + private final FilterTeamStatusUsecase filterTeamStatusUsecase; + + @GetMapping("/member/{memberId}") + public ResponseEntity getTeamStatus( + @PathVariable Long memberId, + @RequestParam(defaultValue = "기본") String sortBy, + @RequestParam(required = false) List mainCategoryIds, + @RequestParam(required = false) List categoryIds, + @RequestParam(required = false) String taskTitle, + @RequestParam(defaultValue = "0") int page, + @RequestParam(defaultValue = "20") int pageSize) { + + FilterTeamStatusRequest filterRequest = new FilterTeamStatusRequest(sortBy, mainCategoryIds, categoryIds, taskTitle); + Pageable pageable = PageRequest.of(page, pageSize); + + TeamStatusResponse response = loadTeamStatusUsecase.getTeamStatus(memberId, filterRequest, pageable); + return ResponseEntity.ok(response); + } + + @GetMapping("/filter") + public ResponseEntity filterTeamStatus( + @RequestParam(required = false) List mainCategoryIds, + @RequestParam(required = false) List categoryIds, + @RequestParam(required = false) String taskTitle, + @RequestParam(defaultValue = "기본") String sortBy, + @RequestParam(defaultValue = "0") int page, + @RequestParam(defaultValue = "20") int pageSize) { + + FilterTeamStatusRequest filterRequest = new FilterTeamStatusRequest(sortBy, mainCategoryIds, categoryIds, taskTitle); + Pageable pageable = PageRequest.of(page, pageSize); + + TeamStatusResponse response = filterTeamStatusUsecase.filterTeamStatus(filterRequest, pageable); + return ResponseEntity.ok(response); + } +} + 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 91149156..d67f794a 100644 --- a/src/main/java/clap/server/adapter/outbound/persistense/TaskPersistenceAdapter.java +++ b/src/main/java/clap/server/adapter/outbound/persistense/TaskPersistenceAdapter.java @@ -119,4 +119,5 @@ public Slice findTaskBoardByFilter(Long processorId, List stat } + } diff --git a/src/main/java/clap/server/adapter/outbound/persistense/mapper/TaskPersistenceMapper.java b/src/main/java/clap/server/adapter/outbound/persistense/mapper/TaskPersistenceMapper.java index 08387c40..053d8290 100644 --- a/src/main/java/clap/server/adapter/outbound/persistense/mapper/TaskPersistenceMapper.java +++ b/src/main/java/clap/server/adapter/outbound/persistense/mapper/TaskPersistenceMapper.java @@ -4,7 +4,6 @@ import clap.server.adapter.outbound.persistense.mapper.common.PersistenceMapper; import clap.server.domain.model.task.Task; import org.mapstruct.Mapper; -import org.mapstruct.Mapping; @Mapper(componentModel = "spring", uses = {MemberPersistenceMapper.class, LabelPersistenceMapper.class, CategoryPersistenceMapper.class}) public interface TaskPersistenceMapper extends PersistenceMapper { 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 9b7607d5..004518f8 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 @@ -2,11 +2,12 @@ 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.request.FilterTeamStatusRequest; +import clap.server.adapter.inbound.web.dto.task.response.TeamMemberTaskResponse; 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; @@ -18,4 +19,5 @@ public interface TaskCustomRepository { Page findAllTasks(Pageable pageable, FilterTaskListRequest findTaskListRequest); List findTasksByFilter(Long processorId, List statuses, LocalDateTime localDateTime, FilterTaskBoardRequest request, Pageable pageable); Page findTasksAssignedByManager(Long processorId, Pageable pageable, FilterTaskListRequest findTaskListRequest); + List findTeamStatus(Long memberId, FilterTeamStatusRequest filter, Pageable pageable); } 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 a9efda36..7587914b 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 @@ -1,13 +1,19 @@ 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.inbound.web.dto.task.request.FilterTeamStatusRequest; +import clap.server.adapter.inbound.web.dto.task.response.TaskItemResponse; +import clap.server.adapter.inbound.web.dto.task.response.TeamMemberTaskResponse; 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 jakarta.persistence.EntityManager; +import jakarta.persistence.TypedQuery; import lombok.RequiredArgsConstructor; import org.springframework.data.domain.Page; import org.springframework.data.domain.PageImpl; @@ -15,7 +21,9 @@ import org.springframework.stereotype.Repository; import java.time.LocalDateTime; +import java.util.Comparator; import java.util.List; +import java.util.stream.Collectors; import static clap.server.adapter.outbound.persistense.entity.task.QTaskEntity.taskEntity; import static com.querydsl.core.types.Order.*; @@ -25,7 +33,7 @@ public class TaskCustomRepositoryImpl implements TaskCustomRepository { private final JPAQueryFactory queryFactory; - + private final EntityManager entityManager; @Override public Page findTasksRequestedByUser(Long requesterId, Pageable pageable, FilterTaskListRequest filterTaskListRequest) { @@ -48,6 +56,99 @@ public Page findTasksAssignedByManager(Long processorId, Pageable pa return getTasksPage(pageable, builder, filterTaskListRequest.sortBy(), filterTaskListRequest.sortDirection()); } + @Override + public List findTeamStatus(Long memberId, FilterTeamStatusRequest filter, Pageable pageable) { + // Build the query string using a separate method + String queryStr = buildQueryString(filter); + + TypedQuery query = entityManager.createQuery(queryStr, TaskEntity.class) + .setParameter("memberId", memberId); + + // Set the parameters + setQueryParameters(query, filter); + + // Apply pagination + query.setMaxResults(pageable.getPageSize()) + .setFirstResult((int) pageable.getOffset()); + + List taskEntities = query.getResultList(); + + return taskEntities.stream() + .collect(Collectors.groupingBy(t -> t.getProcessor().getMemberId())) + .entrySet() + .stream() + .map(entry -> { + List taskResponses = entry.getValue().stream() + .map(taskEntity -> new TaskItemResponse( + taskEntity.getTaskId(), + taskEntity.getTaskCode(), + taskEntity.getTitle(), + taskEntity.getCategory().getMainCategory().getName(), + taskEntity.getCategory().getName(), + taskEntity.getRequester().getNickname(), + taskEntity.getRequester().getImageUrl(), + taskEntity.getRequester().getDepartment().getName(), + taskEntity.getProcessorOrder(), + taskEntity.getTaskStatus(), + taskEntity.getCreatedAt() + )).collect(Collectors.toList()); + + return new TeamMemberTaskResponse( + entry.getKey(), + entry.getValue().get(0).getProcessor().getNickname(), + entry.getValue().get(0).getProcessor().getImageUrl(), + entry.getValue().get(0).getProcessor().getDepartment().getName(), + (int) entry.getValue().stream().filter(t -> t.getTaskStatus() == TaskStatus.IN_PROGRESS).count(), + (int) entry.getValue().stream().filter(t -> t.getTaskStatus() == TaskStatus.PENDING_COMPLETED).count(), + entry.getValue().size(), + taskResponses + ); + }) + .sorted("기여도순".equals(filter.sortBy()) ? + Comparator.comparingInt((TeamMemberTaskResponse t) -> t.inProgressTaskCount() + t.pendingTaskCount()).reversed() : + Comparator.comparing(TeamMemberTaskResponse::nickname)) + .collect(Collectors.toList()); + } + + private String buildQueryString(FilterTeamStatusRequest filter) { + StringBuilder queryStr = new StringBuilder("SELECT t FROM TaskEntity t " + + "JOIN FETCH t.processor p " + + "WHERE (:memberId IS NULL OR p.memberId = :memberId) "); + + if (!filter.taskTitle().isEmpty()) { + queryStr.append("AND t.title LIKE :title "); + } + if (!filter.mainCategoryIds().isEmpty()) { + queryStr.append("AND t.category.mainCategory.id IN :mainCategories "); + } + if (!filter.categoryIds().isEmpty()) { + queryStr.append("AND t.category.id IN :categories "); + } + + if ("기여도순".equals(filter.sortBy())) { + queryStr.append("ORDER BY (SELECT COUNT(te) FROM TaskEntity te WHERE te.processor = p AND te.taskStatus IN ('IN_PROGRESS', 'PENDING_COMPLETED')) DESC"); + } else { + queryStr.append("ORDER BY p.nickname ASC"); + } + + return queryStr.toString(); + } + + private boolean isValidTitle(FilterTeamStatusRequest filter) { + return filter.taskTitle() != null && !filter.taskTitle().isEmpty(); + } + + private void setQueryParameters(TypedQuery query, FilterTeamStatusRequest filter) { + if (isValidTitle(filter)) { + query.setParameter("title", "%" + filter.taskTitle() + "%"); + } + if (!filter.mainCategoryIds().isEmpty()) { + query.setParameter("mainCategories", filter.mainCategoryIds()); + } + if (!filter.categoryIds().isEmpty()) { + query.setParameter("categories", filter.categoryIds()); + } + } @Override public Page findPendingApprovalTasks(Pageable pageable, FilterTaskListRequest filterTaskListRequest) { 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 99099b28..b56766bb 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 @@ -45,6 +45,7 @@ Slice findTasksWithTaskStatusAndCompletedAt( Optional findTopByProcessor_MemberIdAndTaskStatusAndProcessorOrderAfterOrderByProcessorOrderDesc( Long processorId, TaskStatus taskStatus, Long processorOrder); + } diff --git a/src/main/java/clap/server/application/mapper/TaskMapper.java b/src/main/java/clap/server/application/mapper/TaskMapper.java index 2a88dab0..891fe6c3 100644 --- a/src/main/java/clap/server/application/mapper/TaskMapper.java +++ b/src/main/java/clap/server/application/mapper/TaskMapper.java @@ -4,6 +4,8 @@ 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.inbound.web.dto.task.response.TeamMemberTaskResponse; +import clap.server.adapter.outbound.persistense.entity.task.TaskEntity; import clap.server.adapter.outbound.persistense.entity.task.constant.TaskStatus; import clap.server.domain.model.task.Attachment; import clap.server.domain.model.task.Task; @@ -128,7 +130,7 @@ public static TaskBoardResponse toSliceTaskItemResponse(Slice tasks) { ); } - private static TaskItemResponse toTaskItemResponse(Task task) { + public static TaskItemResponse toTaskItemResponse(Task task) { return new TaskItemResponse( task.getTaskId(), task.getTaskCode(), diff --git a/src/main/java/clap/server/application/port/inbound/task/FilterTeamStatusUsecase.java b/src/main/java/clap/server/application/port/inbound/task/FilterTeamStatusUsecase.java new file mode 100644 index 00000000..8fb46282 --- /dev/null +++ b/src/main/java/clap/server/application/port/inbound/task/FilterTeamStatusUsecase.java @@ -0,0 +1,10 @@ +package clap.server.application.port.inbound.task; + + +import clap.server.adapter.inbound.web.dto.task.request.FilterTeamStatusRequest; +import clap.server.adapter.inbound.web.dto.task.response.TeamStatusResponse; +import org.springframework.data.domain.Pageable; + +public interface FilterTeamStatusUsecase { + TeamStatusResponse filterTeamStatus(FilterTeamStatusRequest filter, Pageable pageable); +} diff --git a/src/main/java/clap/server/application/port/inbound/task/LoadTeamStatusUsecase.java b/src/main/java/clap/server/application/port/inbound/task/LoadTeamStatusUsecase.java new file mode 100644 index 00000000..68b204f4 --- /dev/null +++ b/src/main/java/clap/server/application/port/inbound/task/LoadTeamStatusUsecase.java @@ -0,0 +1,10 @@ +package clap.server.application.port.inbound.task; + +import clap.server.adapter.inbound.web.dto.task.request.FilterTeamStatusRequest; +import clap.server.adapter.inbound.web.dto.task.response.TeamStatusResponse; +import org.springframework.data.domain.Pageable; + +public interface LoadTeamStatusUsecase { + TeamStatusResponse getTeamStatus(Long memberId, FilterTeamStatusRequest filter, Pageable pageable); +} + diff --git a/src/main/java/clap/server/application/service/task/TeamStatusService.java b/src/main/java/clap/server/application/service/task/TeamStatusService.java new file mode 100644 index 00000000..46e069d7 --- /dev/null +++ b/src/main/java/clap/server/application/service/task/TeamStatusService.java @@ -0,0 +1,36 @@ +package clap.server.application.service.task; + +import clap.server.adapter.inbound.web.dto.task.request.FilterTeamStatusRequest; +import clap.server.adapter.inbound.web.dto.task.response.TeamMemberTaskResponse; +import clap.server.adapter.inbound.web.dto.task.response.TeamStatusResponse; +import clap.server.adapter.outbound.persistense.repository.task.TaskCustomRepository; +import clap.server.application.port.inbound.task.FilterTeamStatusUsecase; +import clap.server.application.port.inbound.task.LoadTeamStatusUsecase; +import org.springframework.beans.factory.annotation.Qualifier; +import org.springframework.data.domain.Pageable; +import org.springframework.stereotype.Service; + +import java.util.List; + +@Service +public class TeamStatusService implements LoadTeamStatusUsecase, FilterTeamStatusUsecase { + + private final TaskCustomRepository taskCustomRepository; + + public TeamStatusService(@Qualifier("taskCustomRepositoryImpl") TaskCustomRepository taskCustomRepository) { + this.taskCustomRepository = taskCustomRepository; + } + + @Override + public TeamStatusResponse getTeamStatus(Long memberId, FilterTeamStatusRequest filter, Pageable pageable) { + List members = taskCustomRepository.findTeamStatus(memberId, filter, pageable); + return new TeamStatusResponse(members, pageable.getPageNumber(), pageable.getPageSize()); + } + + @Override + public TeamStatusResponse filterTeamStatus(FilterTeamStatusRequest filter, Pageable pageable) { + List members = taskCustomRepository.findTeamStatus(null, filter, pageable); + return new TeamStatusResponse(members, pageable.getPageNumber(), pageable.getPageSize()); + } + +} 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 fd076ac7..c6e96f14 100644 --- a/src/main/java/clap/server/domain/model/task/Task.java +++ b/src/main/java/clap/server/domain/model/task/Task.java @@ -126,4 +126,6 @@ else if (prevTaskOrder != null && nextTaskOrder != null) { this.processorOrder = newProcessorOrder; } + + } From c139cde4561431f99eae4c1a5aa6956e1a151ca8 Mon Sep 17 00:00:00 2001 From: nano-mm Date: Mon, 3 Feb 2025 12:07:31 +0900 Subject: [PATCH 2/4] =?UTF-8?q?CLAP-72=20fix:=20modelattribute=EB=A1=9C=20?= =?UTF-8?q?=EA=B0=9D=EC=B2=B4=20=EB=B0=94=EC=9D=B8=EB=94=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../dto/task/response/TeamStatusResponse.java | 15 ++----- .../web/task/TeamStatusController.java | 44 +++---------------- .../persistense/TaskPersistenceAdapter.java | 5 +++ .../repository/task/TaskCustomRepository.java | 2 +- .../task/TaskCustomRepositoryImpl.java | 12 +---- .../repository/task/TaskRepository.java | 6 +++ .../inbound/task/FilterTeamStatusUsecase.java | 2 +- .../port/outbound/task/LoadTaskPort.java | 6 ++- .../service/task/TeamStatusService.java | 18 ++++---- 9 files changed, 38 insertions(+), 72 deletions(-) diff --git a/src/main/java/clap/server/adapter/inbound/web/dto/task/response/TeamStatusResponse.java b/src/main/java/clap/server/adapter/inbound/web/dto/task/response/TeamStatusResponse.java index 59012222..61cd5dfc 100644 --- a/src/main/java/clap/server/adapter/inbound/web/dto/task/response/TeamStatusResponse.java +++ b/src/main/java/clap/server/adapter/inbound/web/dto/task/response/TeamStatusResponse.java @@ -3,18 +3,9 @@ import java.util.List; public record TeamStatusResponse( - List members, - boolean hasNext, - boolean isFirst, - boolean isLast + List members ) { - public TeamStatusResponse(List members, int pageNumber, int pageSize) { - this( - (members == null) ? List.of() : members, - (members != null && members.size() > pageSize), - pageNumber == 0, - (members == null || members.size() <= pageSize) - ); + public TeamStatusResponse(List members) { + this.members = (members == null) ? List.of() : members; } } - diff --git a/src/main/java/clap/server/adapter/inbound/web/task/TeamStatusController.java b/src/main/java/clap/server/adapter/inbound/web/task/TeamStatusController.java index ac1607e6..3f234903 100644 --- a/src/main/java/clap/server/adapter/inbound/web/task/TeamStatusController.java +++ b/src/main/java/clap/server/adapter/inbound/web/task/TeamStatusController.java @@ -3,18 +3,13 @@ import clap.server.adapter.inbound.web.dto.task.request.FilterTeamStatusRequest; import clap.server.adapter.inbound.web.dto.task.response.TeamStatusResponse; -import clap.server.application.port.inbound.task.FilterTeamStatusUsecase; -import clap.server.application.port.inbound.task.LoadTeamStatusUsecase; +import clap.server.application.service.task.TeamStatusService; import clap.server.common.annotation.architecture.WebAdapter; 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.http.ResponseEntity; import org.springframework.web.bind.annotation.*; -import java.util.List; - @RestController @RequestMapping("/api/team-status") @RequiredArgsConstructor @@ -22,40 +17,11 @@ @WebAdapter public class TeamStatusController { - private final LoadTeamStatusUsecase loadTeamStatusUsecase; - private final FilterTeamStatusUsecase filterTeamStatusUsecase; - - @GetMapping("/member/{memberId}") - public ResponseEntity getTeamStatus( - @PathVariable Long memberId, - @RequestParam(defaultValue = "기본") String sortBy, - @RequestParam(required = false) List mainCategoryIds, - @RequestParam(required = false) List categoryIds, - @RequestParam(required = false) String taskTitle, - @RequestParam(defaultValue = "0") int page, - @RequestParam(defaultValue = "20") int pageSize) { - - FilterTeamStatusRequest filterRequest = new FilterTeamStatusRequest(sortBy, mainCategoryIds, categoryIds, taskTitle); - Pageable pageable = PageRequest.of(page, pageSize); - - TeamStatusResponse response = loadTeamStatusUsecase.getTeamStatus(memberId, filterRequest, pageable); - return ResponseEntity.ok(response); - } + private final TeamStatusService teamStatusService; @GetMapping("/filter") - public ResponseEntity filterTeamStatus( - @RequestParam(required = false) List mainCategoryIds, - @RequestParam(required = false) List categoryIds, - @RequestParam(required = false) String taskTitle, - @RequestParam(defaultValue = "기본") String sortBy, - @RequestParam(defaultValue = "0") int page, - @RequestParam(defaultValue = "20") int pageSize) { - - FilterTeamStatusRequest filterRequest = new FilterTeamStatusRequest(sortBy, mainCategoryIds, categoryIds, taskTitle); - Pageable pageable = PageRequest.of(page, pageSize); - - TeamStatusResponse response = filterTeamStatusUsecase.filterTeamStatus(filterRequest, pageable); + public ResponseEntity filterTeamStatus(@ModelAttribute FilterTeamStatusRequest filter) { + TeamStatusResponse response = teamStatusService.filterTeamStatus(filter); return ResponseEntity.ok(response); } -} - +} \ No newline at end of file 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 d67f794a..1612fd73 100644 --- a/src/main/java/clap/server/adapter/outbound/persistense/TaskPersistenceAdapter.java +++ b/src/main/java/clap/server/adapter/outbound/persistense/TaskPersistenceAdapter.java @@ -6,6 +6,8 @@ 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.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; import clap.server.adapter.outbound.persistense.entity.task.constant.TaskStatus; import clap.server.adapter.outbound.persistense.mapper.TaskPersistenceMapper; @@ -118,6 +120,9 @@ public Slice findTaskBoardByFilter(Long processorId, List stat return new SliceImpl<>(taskList, pageable, hasNext); } + public List findTeamStatus(Long memberId, FilterTeamStatusRequest filter) { + return taskRepository.findTeamStatus(memberId, filter); + } } 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 004518f8..6972fc58 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 @@ -19,5 +19,5 @@ public interface TaskCustomRepository { Page findAllTasks(Pageable pageable, FilterTaskListRequest findTaskListRequest); List findTasksByFilter(Long processorId, List statuses, LocalDateTime localDateTime, FilterTaskBoardRequest request, Pageable pageable); Page findTasksAssignedByManager(Long processorId, Pageable pageable, FilterTaskListRequest findTaskListRequest); - List findTeamStatus(Long memberId, FilterTeamStatusRequest filter, Pageable pageable); + List findTeamStatus(Long memberId, FilterTeamStatusRequest filter); } 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 7587914b..96da5230 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 @@ -1,6 +1,5 @@ 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.inbound.web.dto.task.request.FilterTeamStatusRequest; @@ -56,23 +55,16 @@ public Page findTasksAssignedByManager(Long processorId, Pageable pa return getTasksPage(pageable, builder, filterTaskListRequest.sortBy(), filterTaskListRequest.sortDirection()); } + @Override - public List findTeamStatus(Long memberId, FilterTeamStatusRequest filter, Pageable pageable) { - // Build the query string using a separate method + public List findTeamStatus(Long memberId, FilterTeamStatusRequest filter) { String queryStr = buildQueryString(filter); - TypedQuery query = entityManager.createQuery(queryStr, TaskEntity.class) .setParameter("memberId", memberId); - // Set the parameters setQueryParameters(query, filter); - // Apply pagination - query.setMaxResults(pageable.getPageSize()) - .setFirstResult((int) pageable.getOffset()); - List taskEntities = query.getResultList(); - return taskEntities.stream() .collect(Collectors.groupingBy(t -> t.getProcessor().getMemberId())) .entrySet() 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 b56766bb..52837a33 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 @@ -1,6 +1,8 @@ package clap.server.adapter.outbound.persistense.repository.task; +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; import clap.server.adapter.outbound.persistense.entity.task.constant.TaskStatus; import io.lettuce.core.dynamic.annotation.Param; @@ -45,6 +47,10 @@ Slice findTasksWithTaskStatusAndCompletedAt( Optional findTopByProcessor_MemberIdAndTaskStatusAndProcessorOrderAfterOrderByProcessorOrderDesc( Long processorId, TaskStatus taskStatus, Long processorOrder); + @Query("SELECT t FROM TaskEntity t JOIN FETCH t.processor p WHERE (:memberId IS NULL OR p.memberId = :memberId) ") + List findTeamStatus(@Param("memberId") Long memberId, FilterTeamStatusRequest filter); + + } diff --git a/src/main/java/clap/server/application/port/inbound/task/FilterTeamStatusUsecase.java b/src/main/java/clap/server/application/port/inbound/task/FilterTeamStatusUsecase.java index 8fb46282..646a9975 100644 --- a/src/main/java/clap/server/application/port/inbound/task/FilterTeamStatusUsecase.java +++ b/src/main/java/clap/server/application/port/inbound/task/FilterTeamStatusUsecase.java @@ -6,5 +6,5 @@ import org.springframework.data.domain.Pageable; public interface FilterTeamStatusUsecase { - TeamStatusResponse filterTeamStatus(FilterTeamStatusRequest filter, Pageable pageable); + TeamStatusResponse filterTeamStatus(FilterTeamStatusRequest filter); } 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 f400e726..73d46445 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 @@ -6,6 +6,8 @@ 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.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; @@ -38,4 +40,6 @@ public interface LoadTaskPort { Optional findNextOrderTaskByProcessorIdAndStatus(Long processorId, TaskStatus taskStatus, Long processorOrder); Slice findTaskBoardByFilter(Long processorId, List statuses, LocalDateTime untilDateTime, FilterTaskBoardRequest request, Pageable pageable); -} \ No newline at end of file + + List findTeamStatus(Long memberId, FilterTeamStatusRequest filter); // Pageable 삭제 +} diff --git a/src/main/java/clap/server/application/service/task/TeamStatusService.java b/src/main/java/clap/server/application/service/task/TeamStatusService.java index 46e069d7..b3b8b257 100644 --- a/src/main/java/clap/server/application/service/task/TeamStatusService.java +++ b/src/main/java/clap/server/application/service/task/TeamStatusService.java @@ -3,9 +3,11 @@ import clap.server.adapter.inbound.web.dto.task.request.FilterTeamStatusRequest; import clap.server.adapter.inbound.web.dto.task.response.TeamMemberTaskResponse; import clap.server.adapter.inbound.web.dto.task.response.TeamStatusResponse; +import clap.server.adapter.outbound.persistense.TaskPersistenceAdapter; import clap.server.adapter.outbound.persistense.repository.task.TaskCustomRepository; import clap.server.application.port.inbound.task.FilterTeamStatusUsecase; import clap.server.application.port.inbound.task.LoadTeamStatusUsecase; +import clap.server.application.port.outbound.task.LoadTaskPort; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.data.domain.Pageable; import org.springframework.stereotype.Service; @@ -15,22 +17,22 @@ @Service public class TeamStatusService implements LoadTeamStatusUsecase, FilterTeamStatusUsecase { - private final TaskCustomRepository taskCustomRepository; + private final LoadTaskPort loadTaskPort; // LoadTaskPort를 통해 Repository 접근 - public TeamStatusService(@Qualifier("taskCustomRepositoryImpl") TaskCustomRepository taskCustomRepository) { - this.taskCustomRepository = taskCustomRepository; + public TeamStatusService(LoadTaskPort loadTaskPort) { + this.loadTaskPort = loadTaskPort; } @Override public TeamStatusResponse getTeamStatus(Long memberId, FilterTeamStatusRequest filter, Pageable pageable) { - List members = taskCustomRepository.findTeamStatus(memberId, filter, pageable); - return new TeamStatusResponse(members, pageable.getPageNumber(), pageable.getPageSize()); + List members = loadTaskPort.findTeamStatus(memberId, filter); // 페이징 없음 + return new TeamStatusResponse(members); } @Override - public TeamStatusResponse filterTeamStatus(FilterTeamStatusRequest filter, Pageable pageable) { - List members = taskCustomRepository.findTeamStatus(null, filter, pageable); - return new TeamStatusResponse(members, pageable.getPageNumber(), pageable.getPageSize()); + public TeamStatusResponse filterTeamStatus(FilterTeamStatusRequest filter) { + List members = loadTaskPort.findTeamStatus(null, filter); + return new TeamStatusResponse(members); } } From c810742bc5a091cae83e0f730f0aacfcbf81b243 Mon Sep 17 00:00:00 2001 From: nano-mm Date: Mon, 3 Feb 2025 13:21:04 +0900 Subject: [PATCH 3/4] =?UTF-8?q?CLAP-72=20fix:=20jpal=20=EC=BF=BC=EB=A6=AC?= =?UTF-8?q?=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../task/request/FilterTeamStatusRequest.java | 11 +++- .../task/response/TeamMemberTaskResponse.java | 3 ++ .../web/task/TeamStatusController.java | 6 ++- .../persistense/TaskPersistenceAdapter.java | 5 +- .../repository/task/TaskCustomRepository.java | 2 +- .../task/TaskCustomRepositoryImpl.java | 50 +++++++++++++------ .../repository/task/TaskRepository.java | 3 +- .../inbound/task/FilterTeamStatusUsecase.java | 2 +- .../port/outbound/task/LoadTaskPort.java | 2 +- .../service/task/TeamStatusService.java | 11 ++-- 10 files changed, 65 insertions(+), 30 deletions(-) diff --git a/src/main/java/clap/server/adapter/inbound/web/dto/task/request/FilterTeamStatusRequest.java b/src/main/java/clap/server/adapter/inbound/web/dto/task/request/FilterTeamStatusRequest.java index 43ddbcb4..9fa71e91 100644 --- a/src/main/java/clap/server/adapter/inbound/web/dto/task/request/FilterTeamStatusRequest.java +++ b/src/main/java/clap/server/adapter/inbound/web/dto/task/request/FilterTeamStatusRequest.java @@ -7,10 +7,13 @@ public record FilterTeamStatusRequest( @Schema(description = "정렬 기준 (기여도순, 기본)", example = "기여도순") String sortBy, + @Schema(description = "1차 카테고리 ID 목록", example = "[10, 20, 30]") List mainCategoryIds, + @Schema(description = "2차 카테고리 ID 목록", example = "[1, 2, 3]") List categoryIds, + @Schema(description = "작업 타이틀 검색", example = "타이틀1") String taskTitle ) { @@ -20,6 +23,10 @@ public record FilterTeamStatusRequest( categoryIds = categoryIds == null ? List.of() : categoryIds; taskTitle = taskTitle == null ? "" : taskTitle; } -} - + // 카테고리 유효성 검사 + public boolean isValid() { + // 1차 카테고리가 없으면 2차 카테고리는 선택할 수 없으므로 + return mainCategoryIds.isEmpty() || !categoryIds.isEmpty(); + } +} diff --git a/src/main/java/clap/server/adapter/inbound/web/dto/task/response/TeamMemberTaskResponse.java b/src/main/java/clap/server/adapter/inbound/web/dto/task/response/TeamMemberTaskResponse.java index e01ce75f..157993a4 100644 --- a/src/main/java/clap/server/adapter/inbound/web/dto/task/response/TeamMemberTaskResponse.java +++ b/src/main/java/clap/server/adapter/inbound/web/dto/task/response/TeamMemberTaskResponse.java @@ -1,5 +1,7 @@ package clap.server.adapter.inbound.web.dto.task.response; +import com.querydsl.core.annotations.QueryProjection; + import java.util.List; public record TeamMemberTaskResponse( @@ -12,6 +14,7 @@ public record TeamMemberTaskResponse( int totalTaskCount, List tasks ) { + @QueryProjection public TeamMemberTaskResponse { tasks = (tasks == null) ? List.of() : tasks; } diff --git a/src/main/java/clap/server/adapter/inbound/web/task/TeamStatusController.java b/src/main/java/clap/server/adapter/inbound/web/task/TeamStatusController.java index 3f234903..7e6133a9 100644 --- a/src/main/java/clap/server/adapter/inbound/web/task/TeamStatusController.java +++ b/src/main/java/clap/server/adapter/inbound/web/task/TeamStatusController.java @@ -6,7 +6,9 @@ import clap.server.application.service.task.TeamStatusService; import clap.server.common.annotation.architecture.WebAdapter; import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.validation.Valid; import lombok.RequiredArgsConstructor; +import org.springframework.data.domain.Pageable; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.*; @@ -20,8 +22,8 @@ public class TeamStatusController { private final TeamStatusService teamStatusService; @GetMapping("/filter") - public ResponseEntity filterTeamStatus(@ModelAttribute FilterTeamStatusRequest filter) { - TeamStatusResponse response = teamStatusService.filterTeamStatus(filter); + public ResponseEntity filterTeamStatus(@Valid@ModelAttribute FilterTeamStatusRequest filter, Pageable pageable) { + TeamStatusResponse response = teamStatusService.filterTeamStatus(filter, pageable); return ResponseEntity.ok(response); } } \ No newline at end of file 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 1612fd73..8704a864 100644 --- a/src/main/java/clap/server/adapter/outbound/persistense/TaskPersistenceAdapter.java +++ b/src/main/java/clap/server/adapter/outbound/persistense/TaskPersistenceAdapter.java @@ -120,8 +120,9 @@ public Slice findTaskBoardByFilter(Long processorId, List stat return new SliceImpl<>(taskList, pageable, hasNext); } - public List findTeamStatus(Long memberId, FilterTeamStatusRequest filter) { - return taskRepository.findTeamStatus(memberId, filter); + @Override + public Page findTeamStatus(Long memberId, FilterTeamStatusRequest filter, Pageable pageable) { + return taskRepository.findTeamStatus(memberId, filter, pageable); } 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 6972fc58..c0cf79d4 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 @@ -15,9 +15,9 @@ public interface TaskCustomRepository { Page findTasksRequestedByUser(Long requesterId, Pageable pageable, FilterTaskListRequest findTaskListRequest); + Page findTeamStatus(Long memberId, FilterTeamStatusRequest filter, Pageable pageable); Page findPendingApprovalTasks(Pageable pageable, FilterTaskListRequest findTaskListRequest); Page findAllTasks(Pageable pageable, FilterTaskListRequest findTaskListRequest); List findTasksByFilter(Long processorId, List statuses, LocalDateTime localDateTime, FilterTaskBoardRequest request, Pageable pageable); Page findTasksAssignedByManager(Long processorId, Pageable pageable, FilterTaskListRequest findTaskListRequest); - List findTeamStatus(Long memberId, FilterTeamStatusRequest filter); } 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 96da5230..b2aa6cbf 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 @@ -20,7 +20,6 @@ import org.springframework.stereotype.Repository; import java.time.LocalDateTime; -import java.util.Comparator; import java.util.List; import java.util.stream.Collectors; @@ -57,18 +56,33 @@ public Page findTasksAssignedByManager(Long processorId, Pageable pa } @Override - public List findTeamStatus(Long memberId, FilterTeamStatusRequest filter) { - String queryStr = buildQueryString(filter); - TypedQuery query = entityManager.createQuery(queryStr, TaskEntity.class) - .setParameter("memberId", memberId); + public Page findTeamStatus(Long memberId, FilterTeamStatusRequest filter, Pageable pageable) { + // 1. 담당자 목록을 먼저 페이징해서 가져옴 + List processorIds = queryFactory + .select(taskEntity.processor.memberId) + .from(taskEntity) + .groupBy(taskEntity.processor.memberId) + .orderBy("기여도순".equals(filter.sortBy()) ? + taskEntity.taskId.count().desc() : + taskEntity.processor.nickname.asc()) + .offset(pageable.getOffset()) + .limit(pageable.getPageSize()) + .fetch(); - setQueryParameters(query, filter); + if (processorIds.isEmpty()) { + return Page.empty(pageable); + } + + // 2. 담당자별 작업 조회 + List taskEntities = queryFactory + .selectFrom(taskEntity) + .where(taskEntity.processor.memberId.in(processorIds)) + .fetch(); - List taskEntities = query.getResultList(); - return taskEntities.stream() + // 3. 담당자별 그룹핑 + List teamResponses = taskEntities.stream() .collect(Collectors.groupingBy(t -> t.getProcessor().getMemberId())) - .entrySet() - .stream() + .entrySet().stream() .map(entry -> { List taskResponses = entry.getValue().stream() .map(taskEntity -> new TaskItemResponse( @@ -95,13 +109,19 @@ public List findTeamStatus(Long memberId, FilterTeamStat entry.getValue().size(), taskResponses ); - }) - .sorted("기여도순".equals(filter.sortBy()) ? - Comparator.comparingInt((TeamMemberTaskResponse t) -> t.inProgressTaskCount() + t.pendingTaskCount()).reversed() : - Comparator.comparing(TeamMemberTaskResponse::nickname)) - .collect(Collectors.toList()); + }).collect(Collectors.toList()); + + long total = queryFactory + .select(taskEntity.processor.memberId) + .from(taskEntity) + .groupBy(taskEntity.processor.memberId) + .fetchCount(); + + return new PageImpl<>(teamResponses, pageable, total); } + + private String buildQueryString(FilterTeamStatusRequest filter) { StringBuilder queryStr = new StringBuilder("SELECT t FROM TaskEntity t " + "JOIN FETCH t.processor p " + 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 52837a33..b92d504c 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 @@ -6,6 +6,7 @@ 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.Page; import org.springframework.data.domain.Pageable; import org.springframework.data.domain.Slice; import org.springframework.data.jpa.repository.JpaRepository; @@ -48,7 +49,7 @@ Optional findTopByProcessor_MemberIdAndTaskStatusAndProcessorOrderAf Long processorId, TaskStatus taskStatus, Long processorOrder); @Query("SELECT t FROM TaskEntity t JOIN FETCH t.processor p WHERE (:memberId IS NULL OR p.memberId = :memberId) ") - List findTeamStatus(@Param("memberId") Long memberId, FilterTeamStatusRequest filter); + Page findTeamStatus(@Param("memberId") Long memberId, FilterTeamStatusRequest filter, Pageable pageable); diff --git a/src/main/java/clap/server/application/port/inbound/task/FilterTeamStatusUsecase.java b/src/main/java/clap/server/application/port/inbound/task/FilterTeamStatusUsecase.java index 646a9975..8fb46282 100644 --- a/src/main/java/clap/server/application/port/inbound/task/FilterTeamStatusUsecase.java +++ b/src/main/java/clap/server/application/port/inbound/task/FilterTeamStatusUsecase.java @@ -6,5 +6,5 @@ import org.springframework.data.domain.Pageable; public interface FilterTeamStatusUsecase { - TeamStatusResponse filterTeamStatus(FilterTeamStatusRequest filter); + TeamStatusResponse filterTeamStatus(FilterTeamStatusRequest filter, Pageable pageable); } 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 73d46445..c7813e6f 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 @@ -41,5 +41,5 @@ public interface LoadTaskPort { Slice findTaskBoardByFilter(Long processorId, List statuses, LocalDateTime untilDateTime, FilterTaskBoardRequest request, Pageable pageable); - List findTeamStatus(Long memberId, FilterTeamStatusRequest filter); // Pageable 삭제 + Page findTeamStatus(Long memberId, FilterTeamStatusRequest filter, Pageable pageable); } diff --git a/src/main/java/clap/server/application/service/task/TeamStatusService.java b/src/main/java/clap/server/application/service/task/TeamStatusService.java index b3b8b257..6219b242 100644 --- a/src/main/java/clap/server/application/service/task/TeamStatusService.java +++ b/src/main/java/clap/server/application/service/task/TeamStatusService.java @@ -9,6 +9,7 @@ import clap.server.application.port.inbound.task.LoadTeamStatusUsecase; import clap.server.application.port.outbound.task.LoadTaskPort; import org.springframework.beans.factory.annotation.Qualifier; +import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; import org.springframework.stereotype.Service; @@ -25,14 +26,14 @@ public TeamStatusService(LoadTaskPort loadTaskPort) { @Override public TeamStatusResponse getTeamStatus(Long memberId, FilterTeamStatusRequest filter, Pageable pageable) { - List members = loadTaskPort.findTeamStatus(memberId, filter); // 페이징 없음 - return new TeamStatusResponse(members); + Page members = loadTaskPort.findTeamStatus(memberId, filter, pageable); // 페이징 처리 + return new TeamStatusResponse(members.getContent()); // Page에서 List로 변환 } @Override - public TeamStatusResponse filterTeamStatus(FilterTeamStatusRequest filter) { - List members = loadTaskPort.findTeamStatus(null, filter); - return new TeamStatusResponse(members); + public TeamStatusResponse filterTeamStatus(FilterTeamStatusRequest filter, Pageable pageable) { + Page members = loadTaskPort.findTeamStatus(null, filter, pageable); + return new TeamStatusResponse(members.getContent()); } } From a97732244f35df965b435d0787dcbba82de80ab1 Mon Sep 17 00:00:00 2001 From: nano-mm Date: Mon, 3 Feb 2025 14:11:30 +0900 Subject: [PATCH 4/4] =?UTF-8?q?CLAP-72=20fix:=20=ED=8E=98=EC=9D=B4?= =?UTF-8?q?=EC=A7=95=20=EA=B8=B0=EB=8A=A5=20=EC=A0=9C=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../web/task/TeamStatusController.java | 4 ++-- .../persistense/TaskPersistenceAdapter.java | 4 ++-- .../repository/task/TaskCustomRepository.java | 2 +- .../task/TaskCustomRepositoryImpl.java | 20 +++++-------------- .../inbound/task/FilterTeamStatusUsecase.java | 2 +- .../port/outbound/task/LoadTaskPort.java | 2 +- .../service/task/TeamStatusService.java | 16 ++++++--------- 7 files changed, 18 insertions(+), 32 deletions(-) diff --git a/src/main/java/clap/server/adapter/inbound/web/task/TeamStatusController.java b/src/main/java/clap/server/adapter/inbound/web/task/TeamStatusController.java index 7e6133a9..69a559bf 100644 --- a/src/main/java/clap/server/adapter/inbound/web/task/TeamStatusController.java +++ b/src/main/java/clap/server/adapter/inbound/web/task/TeamStatusController.java @@ -22,8 +22,8 @@ public class TeamStatusController { private final TeamStatusService teamStatusService; @GetMapping("/filter") - public ResponseEntity filterTeamStatus(@Valid@ModelAttribute FilterTeamStatusRequest filter, Pageable pageable) { - TeamStatusResponse response = teamStatusService.filterTeamStatus(filter, pageable); + public ResponseEntity filterTeamStatus(@Valid@ModelAttribute FilterTeamStatusRequest filter) { + TeamStatusResponse response = teamStatusService.filterTeamStatus(filter); return ResponseEntity.ok(response); } } \ No newline at end of file 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 8704a864..7fe29da7 100644 --- a/src/main/java/clap/server/adapter/outbound/persistense/TaskPersistenceAdapter.java +++ b/src/main/java/clap/server/adapter/outbound/persistense/TaskPersistenceAdapter.java @@ -121,8 +121,8 @@ public Slice findTaskBoardByFilter(Long processorId, List stat } @Override - public Page findTeamStatus(Long memberId, FilterTeamStatusRequest filter, Pageable pageable) { - return taskRepository.findTeamStatus(memberId, filter, pageable); + public List findTeamStatus(Long memberId, FilterTeamStatusRequest filter) { + return taskRepository.findTeamStatus(memberId, filter); } 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 c0cf79d4..44b3df15 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 @@ -15,7 +15,7 @@ public interface TaskCustomRepository { Page findTasksRequestedByUser(Long requesterId, Pageable pageable, FilterTaskListRequest findTaskListRequest); - Page findTeamStatus(Long memberId, FilterTeamStatusRequest filter, Pageable pageable); + 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); 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 b2aa6cbf..a7b35c98 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 @@ -56,8 +56,8 @@ public Page findTasksAssignedByManager(Long processorId, Pageable pa } @Override - public Page findTeamStatus(Long memberId, FilterTeamStatusRequest filter, Pageable pageable) { - // 1. 담당자 목록을 먼저 페이징해서 가져옴 + public List findTeamStatus(Long memberId, FilterTeamStatusRequest filter) { + // 1. 담당자 목록을 가져옴 (페이징 제거) List processorIds = queryFactory .select(taskEntity.processor.memberId) .from(taskEntity) @@ -65,22 +65,20 @@ public Page findTeamStatus(Long memberId, FilterTeamStat .orderBy("기여도순".equals(filter.sortBy()) ? taskEntity.taskId.count().desc() : taskEntity.processor.nickname.asc()) - .offset(pageable.getOffset()) - .limit(pageable.getPageSize()) .fetch(); if (processorIds.isEmpty()) { - return Page.empty(pageable); + return List.of(); // 결과가 없으면 빈 리스트 반환 } - // 2. 담당자별 작업 조회 + // 2. 담당자별 작업 조회 (페이징 제거) List taskEntities = queryFactory .selectFrom(taskEntity) .where(taskEntity.processor.memberId.in(processorIds)) .fetch(); // 3. 담당자별 그룹핑 - List teamResponses = taskEntities.stream() + return taskEntities.stream() .collect(Collectors.groupingBy(t -> t.getProcessor().getMemberId())) .entrySet().stream() .map(entry -> { @@ -110,14 +108,6 @@ public Page findTeamStatus(Long memberId, FilterTeamStat taskResponses ); }).collect(Collectors.toList()); - - long total = queryFactory - .select(taskEntity.processor.memberId) - .from(taskEntity) - .groupBy(taskEntity.processor.memberId) - .fetchCount(); - - return new PageImpl<>(teamResponses, pageable, total); } diff --git a/src/main/java/clap/server/application/port/inbound/task/FilterTeamStatusUsecase.java b/src/main/java/clap/server/application/port/inbound/task/FilterTeamStatusUsecase.java index 8fb46282..646a9975 100644 --- a/src/main/java/clap/server/application/port/inbound/task/FilterTeamStatusUsecase.java +++ b/src/main/java/clap/server/application/port/inbound/task/FilterTeamStatusUsecase.java @@ -6,5 +6,5 @@ import org.springframework.data.domain.Pageable; public interface FilterTeamStatusUsecase { - TeamStatusResponse filterTeamStatus(FilterTeamStatusRequest filter, Pageable pageable); + TeamStatusResponse filterTeamStatus(FilterTeamStatusRequest filter); } 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 c7813e6f..44ed6e7e 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 @@ -41,5 +41,5 @@ public interface LoadTaskPort { Slice findTaskBoardByFilter(Long processorId, List statuses, LocalDateTime untilDateTime, FilterTaskBoardRequest request, Pageable pageable); - Page findTeamStatus(Long memberId, FilterTeamStatusRequest filter, Pageable pageable); + List findTeamStatus(Long memberId, FilterTeamStatusRequest filter); } diff --git a/src/main/java/clap/server/application/service/task/TeamStatusService.java b/src/main/java/clap/server/application/service/task/TeamStatusService.java index 6219b242..e24d09a6 100644 --- a/src/main/java/clap/server/application/service/task/TeamStatusService.java +++ b/src/main/java/clap/server/application/service/task/TeamStatusService.java @@ -3,13 +3,9 @@ import clap.server.adapter.inbound.web.dto.task.request.FilterTeamStatusRequest; import clap.server.adapter.inbound.web.dto.task.response.TeamMemberTaskResponse; import clap.server.adapter.inbound.web.dto.task.response.TeamStatusResponse; -import clap.server.adapter.outbound.persistense.TaskPersistenceAdapter; -import clap.server.adapter.outbound.persistense.repository.task.TaskCustomRepository; import clap.server.application.port.inbound.task.FilterTeamStatusUsecase; import clap.server.application.port.inbound.task.LoadTeamStatusUsecase; import clap.server.application.port.outbound.task.LoadTaskPort; -import org.springframework.beans.factory.annotation.Qualifier; -import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; import org.springframework.stereotype.Service; @@ -18,7 +14,7 @@ @Service public class TeamStatusService implements LoadTeamStatusUsecase, FilterTeamStatusUsecase { - private final LoadTaskPort loadTaskPort; // LoadTaskPort를 통해 Repository 접근 + private final LoadTaskPort loadTaskPort; public TeamStatusService(LoadTaskPort loadTaskPort) { this.loadTaskPort = loadTaskPort; @@ -26,14 +22,14 @@ public TeamStatusService(LoadTaskPort loadTaskPort) { @Override public TeamStatusResponse getTeamStatus(Long memberId, FilterTeamStatusRequest filter, Pageable pageable) { - Page members = loadTaskPort.findTeamStatus(memberId, filter, pageable); // 페이징 처리 - return new TeamStatusResponse(members.getContent()); // Page에서 List로 변환 + List members = loadTaskPort.findTeamStatus(memberId, filter); // 페이징 처리 + return new TeamStatusResponse(members); } @Override - public TeamStatusResponse filterTeamStatus(FilterTeamStatusRequest filter, Pageable pageable) { - Page members = loadTaskPort.findTeamStatus(null, filter, pageable); - return new TeamStatusResponse(members.getContent()); + public TeamStatusResponse filterTeamStatus(FilterTeamStatusRequest filter) { + List members = loadTaskPort.findTeamStatus(null, filter); + return new TeamStatusResponse(members); } }