Skip to content
Original file line number Diff line number Diff line change
@@ -1,16 +1,21 @@
package clap.server.adapter.inbound.web.dto.task.request;

import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotNull;

import java.util.List;

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

@Schema(description = "1차 카테고리 ID, ** 2차 카테고리로 검색할 시에는 1차 카테고리 값은 넣지 않습니다.")
Long mainCategoryId,
@Schema(description = "1차 카테고리 ID 목록", example = "[1, 3, 5]")
@NotNull
List<Long> mainCategoryIds,

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

@Schema(description = "작업 제목", example = "작업 제목")
String title,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,6 @@
public record TaskBoardResponse(
List<TaskItemResponse> tasksInProgress,
List<TaskItemResponse> tasksPendingComplete,
List<TaskItemResponse> tasksCompleted,
boolean hasNext,
boolean isFirst,
boolean isLast
List<TaskItemResponse> tasksCompleted
){
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package clap.server.adapter.inbound.web.dto.task.response;

import clap.server.adapter.outbound.persistense.entity.task.constant.LabelColor;
import clap.server.adapter.outbound.persistense.entity.task.constant.TaskStatus;

import java.time.LocalDateTime;
Expand All @@ -10,11 +11,19 @@ public record TaskItemResponse(
String title,
String mainCategoryName,
String categoryName,
LabelInfo labelInfo,
String requesterNickname,
String requesterImageUrl,
String requesterDepartment,
long processorOrder,
TaskStatus taskStatus,
LocalDateTime createdAt
){
) {
public static record LabelInfo(
String labelName,
LabelColor labelColor
) {
}

}

Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,9 @@
import java.util.List;

public record TeamStatusResponse(
List<TeamMemberTaskResponse> members
List<TeamTaskResponse> members
) {
public TeamStatusResponse(List<TeamMemberTaskResponse> members) {
public TeamStatusResponse(List<TeamTaskResponse> members) {
this.members = (members == null) ? List.of() : members;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package clap.server.adapter.inbound.web.dto.task.response;

import clap.server.adapter.outbound.persistense.entity.task.constant.TaskStatus;

import java.time.LocalDateTime;

public record TeamTaskItemResponse(
Long taskId,
String taskCode,
String title,
String mainCategoryName,
String categoryName,
String requesterNickname,
String requesterImageUrl,
String requesterDepartment,
long processorOrder,
TaskStatus taskStatus,
LocalDateTime createdAt
) {
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,18 +4,18 @@

import java.util.List;

public record TeamMemberTaskResponse(
public record TeamTaskResponse(
Long processorId,
String nickname,
String imageUrl,
String department,
int inProgressTaskCount,
int pendingTaskCount,
int totalTaskCount,
List<TaskItemResponse> tasks
List<TeamTaskItemResponse> tasks
) {
@QueryProjection
public TeamMemberTaskResponse {
public TeamTaskResponse {
tasks = (tasks == null) ? List.of() : tasks;
}
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
package clap.server.adapter.inbound.web.history;

import clap.server.adapter.inbound.security.SecurityUserDetails;
import clap.server.adapter.inbound.security.service.SecurityUserDetails;
import clap.server.adapter.inbound.web.dto.history.request.EditCommentRequest;
import clap.server.application.port.inbound.history.DeleteCommentUsecase;
import clap.server.application.port.inbound.history.EditCommentUsecase;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
package clap.server.adapter.inbound.web.history;

import clap.server.adapter.inbound.security.SecurityUserDetails;
import clap.server.adapter.inbound.security.service.SecurityUserDetails;
import clap.server.adapter.inbound.web.dto.history.request.CreateCommentRequest;
import clap.server.application.port.inbound.history.SaveCommentAttachmentUsecase;
import clap.server.application.port.inbound.history.SaveCommentUsecase;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,17 +7,13 @@
import clap.server.adapter.outbound.persistense.entity.task.constant.TaskStatus;
import clap.server.application.port.inbound.task.FilterTaskBoardUsecase;
import clap.server.application.port.inbound.task.UpdateTaskBoardUsecase;
import clap.server.application.port.inbound.task.GetTaskBoardUsecase;
import clap.server.application.port.inbound.task.UpdateTaskOrderAndStatusUsecase;
import clap.server.common.annotation.architecture.WebAdapter;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.media.Schema;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.format.annotation.DateTimeFormat;
import org.springframework.http.ResponseEntity;
import org.springframework.security.access.annotation.Secured;
Expand All @@ -26,38 +22,32 @@

import java.time.LocalDate;

@Slf4j
@Tag(name = "02. Task [담당자]")
@WebAdapter
@RestController
@RequiredArgsConstructor
@RequestMapping("/api/task-board")
public class TaskBoardController {
private final GetTaskBoardUsecase getTaskBoardUsecase;
private final FilterTaskBoardUsecase filterTaskBoardUsecase;
private final UpdateTaskBoardUsecase updateTaskBoardUsecase;
private final UpdateTaskOrderAndStatusUsecase updateTaskOrderAndStatus;

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

@Operation(summary = "작업 보드 순서 및 상태 변경 API")
@Secured({"ROLE_MANAGER"})
@PatchMapping
public void updateTaskBoard(@Parameter(description = "전환될 작업의 상태, 상태 전환이 아니라면 입력 X", schema = @Schema(allowableValues = {"IN_PROGRESS", "PENDING_COMPLETED", "COMPLETED"}))
public void updateTaskBoard(@Parameter(description = "전환될 작업의 상태, 상태 전환이 아니라면 입력 X",
schema = @Schema(allowableValues = {"IN_PROGRESS", "PENDING_COMPLETED", "COMPLETED"}))
@RequestParam(required = false) TaskStatus status,
@RequestBody UpdateTaskOrderRequest request,
@AuthenticationPrincipal SecurityUserDetails userInfo) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
package clap.server.adapter.outbound.persistense;

import clap.server.adapter.inbound.web.dto.task.request.FilterTaskListRequest;
import clap.server.adapter.inbound.web.dto.task.request.FilterTaskBoardRequest;
import clap.server.adapter.inbound.web.dto.task.request.FilterTaskListRequest;
import clap.server.adapter.inbound.web.dto.task.request.FilterTeamStatusRequest;
import clap.server.adapter.inbound.web.dto.task.response.TeamMemberTaskResponse;
import clap.server.adapter.inbound.web.dto.task.response.TeamTaskResponse;
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;
Expand All @@ -16,8 +16,6 @@
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Slice;
import org.springframework.data.domain.SliceImpl;

import java.time.LocalDateTime;
import java.util.List;
Expand Down Expand Up @@ -61,12 +59,6 @@ public Page<Task> findPendingApprovalTasks(Pageable pageable, FilterTaskListRequ
.map(taskPersistenceMapper::toDomain);
}

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

@Override
public Optional<Task> findByIdAndStatus(Long id, TaskStatus status) {
Optional<TaskEntity> taskEntity = taskRepository.findByTaskIdAndTaskStatus(id, status);
Expand Down Expand Up @@ -98,21 +90,14 @@ public Optional<Task> findNextOrderTaskByProcessorIdAndStatus(Long processorId,
}

@Override
public Slice<Task> findTaskBoardByFilter(Long processorId, List<TaskStatus> statuses, LocalDateTime untilDate, FilterTaskBoardRequest request, Pageable pageable) {
List<Task> taskList = new java.util.ArrayList<>(taskRepository.findTasksByFilter(processorId, statuses, untilDate, request, pageable)
public List<Task> findTaskBoardByFilter(Long processorId, List<TaskStatus> statuses, LocalDateTime fromDate, FilterTaskBoardRequest request) {
return taskRepository.findTasksByFilter(processorId, statuses, fromDate, request)
.stream()
.map(taskPersistenceMapper::toDomain)
.toList());

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

@Override
public List<TeamMemberTaskResponse> findTeamStatus(Long memberId, FilterTeamStatusRequest filter) {
public List<TeamTaskResponse> findTeamStatus(Long memberId, FilterTeamStatusRequest filter) {
return taskRepository.findTeamStatus(memberId, filter);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
import clap.server.adapter.inbound.web.dto.task.request.FilterTaskListRequest;
import clap.server.adapter.inbound.web.dto.task.request.FilterTaskBoardRequest;
import clap.server.adapter.inbound.web.dto.task.request.FilterTeamStatusRequest;
import clap.server.adapter.inbound.web.dto.task.response.TeamMemberTaskResponse;
import clap.server.adapter.inbound.web.dto.task.response.TeamTaskResponse;
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;
Expand All @@ -15,9 +15,9 @@
public interface TaskCustomRepository {

Page<TaskEntity> findTasksRequestedByUser(Long requesterId, Pageable pageable, FilterTaskListRequest findTaskListRequest);
List<TeamMemberTaskResponse> findTeamStatus(Long memberId, FilterTeamStatusRequest filter);
List<TeamTaskResponse> findTeamStatus(Long memberId, FilterTeamStatusRequest filter);
Page<TaskEntity> findPendingApprovalTasks(Pageable pageable, FilterTaskListRequest findTaskListRequest);
Page<TaskEntity> findAllTasks(Pageable pageable, FilterTaskListRequest findTaskListRequest);
List<TaskEntity> findTasksByFilter(Long processorId, List<TaskStatus> statuses, LocalDateTime localDateTime, FilterTaskBoardRequest request, Pageable pageable);
List<TaskEntity> findTasksByFilter(Long processorId, List<TaskStatus> statuses, LocalDateTime localDateTime, FilterTaskBoardRequest request);
Page<TaskEntity> findTasksAssignedByManager(Long processorId, Pageable pageable, FilterTaskListRequest findTaskListRequest);
}
Original file line number Diff line number Diff line change
@@ -1,18 +1,17 @@
package clap.server.adapter.outbound.persistense.repository.task;

import clap.server.adapter.inbound.web.dto.task.request.FilterTaskListRequest;
import clap.server.adapter.inbound.web.dto.task.request.FilterTaskBoardRequest;
import clap.server.adapter.inbound.web.dto.task.request.FilterTaskListRequest;
import clap.server.adapter.inbound.web.dto.task.request.FilterTeamStatusRequest;
import clap.server.adapter.inbound.web.dto.task.response.TaskItemResponse;
import clap.server.adapter.inbound.web.dto.task.response.TeamMemberTaskResponse;
import clap.server.adapter.inbound.web.dto.task.response.TeamTaskItemResponse;
import clap.server.adapter.inbound.web.dto.task.response.TeamTaskResponse;
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.CaseBuilder;
import com.querydsl.core.types.dsl.DateTimePath;
import com.querydsl.jpa.impl.JPAQueryFactory;
import jakarta.persistence.EntityManager;
import com.querydsl.core.types.dsl.CaseBuilder;
import lombok.RequiredArgsConstructor;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageImpl;
Expand All @@ -32,7 +31,6 @@
public class TaskCustomRepositoryImpl implements TaskCustomRepository {

private final JPAQueryFactory queryFactory;
private final EntityManager entityManager;

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

@Override
public List<TeamMemberTaskResponse> findTeamStatus(Long memberId, FilterTeamStatusRequest filter) {
public List<TeamTaskResponse> findTeamStatus(Long memberId, FilterTeamStatusRequest filter) {
BooleanBuilder builder = new BooleanBuilder();

// 담당자 ID 필터링
Expand Down Expand Up @@ -101,8 +99,8 @@ public List<TeamMemberTaskResponse> findTeamStatus(Long memberId, FilterTeamStat
.collect(Collectors.groupingBy(t -> t.getProcessor().getMemberId()))
.entrySet().stream()
.map(entry -> {
List<TaskItemResponse> taskResponses = entry.getValue().stream()
.map(taskEntity -> new TaskItemResponse(
List<TeamTaskItemResponse> taskResponses = entry.getValue().stream()
.map(taskEntity -> new TeamTaskItemResponse(
taskEntity.getTaskId(),
taskEntity.getTaskCode(),
taskEntity.getTitle(),
Expand All @@ -116,7 +114,7 @@ public List<TeamMemberTaskResponse> findTeamStatus(Long memberId, FilterTeamStat
taskEntity.getCreatedAt()
)).collect(Collectors.toList());

return new TeamMemberTaskResponse(
return new TeamTaskResponse(
entry.getKey(),
entry.getValue().get(0).getProcessor().getNickname(),
entry.getValue().get(0).getProcessor().getImageUrl(),
Expand Down Expand Up @@ -156,32 +154,37 @@ public Page<TaskEntity> findAllTasks(Pageable pageable, FilterTaskListRequest fi
}

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

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

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

if (fromDateTime != null) {
builder.and(taskEntity.taskStatus.ne(TaskStatus.COMPLETED)
.or(taskEntity.taskStatus.eq(TaskStatus.COMPLETED)
.and(taskEntity.finishedAt.goe(fromDateTime))));
}

if (request.labelId() != null) {
builder.and(taskEntity.label.labelId.eq(request.labelId()));
}
if (request.mainCategoryId() != null) {
builder.and(taskEntity.category.mainCategory.categoryId.eq(request.mainCategoryId()));
if (!request.mainCategoryIds().isEmpty()) {
builder.and(taskEntity.category.mainCategory.categoryId.in(request.mainCategoryIds())
.and(taskEntity.category.mainCategory.isDeleted.eq(false)));
}
if (request.subCategoryId() != null) {
builder.and(taskEntity.category.categoryId.eq(request.subCategoryId()));
if (!request.categoryIds().isEmpty()) {
builder.and(taskEntity.category.categoryId.in(request.categoryIds())
.and(taskEntity.category.isDeleted.eq(false)));
}
if (request.title() != null && !request.title().isEmpty()) {
String titleFilter = "%" + request.title() + "%";
Expand Down
Loading