diff --git a/src/main/java/clap/server/adapter/inbound/web/dto/task/AttachmentRequest.java b/src/main/java/clap/server/adapter/inbound/web/dto/task/AttachmentRequest.java index 52c1d9c6..96a038c7 100644 --- a/src/main/java/clap/server/adapter/inbound/web/dto/task/AttachmentRequest.java +++ b/src/main/java/clap/server/adapter/inbound/web/dto/task/AttachmentRequest.java @@ -1,7 +1,13 @@ package clap.server.adapter.inbound.web.dto.task; +import io.swagger.v3.oas.annotations.media.Schema; + public record AttachmentRequest( + @Schema(description = "파일 ID", example = "45") Long fileId, - String fileUrl) { + + @Schema(description = "파일 URL", example = "https://example.com/file.png") + String fileUrl +) { } diff --git a/src/main/java/clap/server/adapter/inbound/web/dto/task/CreateTaskRequest.java b/src/main/java/clap/server/adapter/inbound/web/dto/task/CreateTaskRequest.java index 6e1195e9..df1a3512 100644 --- a/src/main/java/clap/server/adapter/inbound/web/dto/task/CreateTaskRequest.java +++ b/src/main/java/clap/server/adapter/inbound/web/dto/task/CreateTaskRequest.java @@ -1,18 +1,30 @@ package clap.server.adapter.inbound.web.dto.task; +import io.swagger.v3.oas.annotations.media.Schema; import jakarta.validation.constraints.NotBlank; import jakarta.validation.constraints.NotNull; import java.util.List; + +@Schema(description = "작업 생성 요청") public record CreateTaskRequest( + @Schema(description = "카테고리 ID") @NotNull Long categoryId, + + @Schema(description = "메인 카테고리 ID") @NotNull Long mainCategoryId, + + @Schema(description = "작업 제목") @NotBlank String title, + + @Schema(description = "작업 설명") String description, + + @Schema(description = "첨부 파일 URL 목록", example = "[\"https://example.com/file1.png\", \"https://example.com/file2.pdf\"]") List<@NotBlank String> fileUrls ) { } diff --git a/src/main/java/clap/server/adapter/inbound/web/dto/task/CreateAndUpdateTaskResponse.java b/src/main/java/clap/server/adapter/inbound/web/dto/task/CreateTaskResponse.java similarity index 74% rename from src/main/java/clap/server/adapter/inbound/web/dto/task/CreateAndUpdateTaskResponse.java rename to src/main/java/clap/server/adapter/inbound/web/dto/task/CreateTaskResponse.java index bb923fd8..a58f1a79 100644 --- a/src/main/java/clap/server/adapter/inbound/web/dto/task/CreateAndUpdateTaskResponse.java +++ b/src/main/java/clap/server/adapter/inbound/web/dto/task/CreateTaskResponse.java @@ -2,7 +2,7 @@ -public record CreateAndUpdateTaskResponse( +public record CreateTaskResponse( Long taskId, Long categoryId, String title diff --git a/src/main/java/clap/server/adapter/inbound/web/dto/task/FilterTaskListRequest.java b/src/main/java/clap/server/adapter/inbound/web/dto/task/FilterTaskListRequest.java new file mode 100644 index 00000000..3e858ed9 --- /dev/null +++ b/src/main/java/clap/server/adapter/inbound/web/dto/task/FilterTaskListRequest.java @@ -0,0 +1,41 @@ +package clap.server.adapter.inbound.web.dto.task; + + +import clap.server.adapter.outbound.persistense.entity.task.constant.TaskStatus; +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotNull; +import org.springframework.beans.factory.annotation.Value; + +import java.util.List; + +@Schema(description = "작업 필터링 요청") +public record FilterTaskListRequest( + + @Schema(description = "검색 기간 (단위: 시간)", example = "1, 24, 168, 730, 2190 (1시간, 24시간, 1주일, 1개월, 3개월)") + Integer term, + + @Schema(description = "카테고리 ID 목록", example = "[1, 2, 3]") + @NotNull + List categoryIds, + + @Schema(description = "메인 카테고리 ID 목록", example = "[10, 20, 30]") + @NotNull + List mainCategoryIds, + + @Schema(description = "작업 제목", example = "작업 제목") + @NotNull + String title, + + @Schema(description = "사용자 닉네임", example = "atom.park") + @NotNull + String nickName, + + @Schema(description = "작업 상태 목록", example = "[\"REQUESTED\", \"IN_PROGRESS\"]") + @NotNull + List taskStatus, + + @Schema(description = "정렬 기준", implementation = OrderRequest.class) + @NotNull + OrderRequest orderRequest +) { +} diff --git a/src/main/java/clap/server/adapter/inbound/web/dto/task/FindTaskListRequest.java b/src/main/java/clap/server/adapter/inbound/web/dto/task/FindTaskListRequest.java deleted file mode 100644 index 13fe72cb..00000000 --- a/src/main/java/clap/server/adapter/inbound/web/dto/task/FindTaskListRequest.java +++ /dev/null @@ -1,14 +0,0 @@ -package clap.server.adapter.inbound.web.dto.task; - - -import clap.server.adapter.outbound.persistense.entity.task.constant.TaskStatus; - -public record FindTaskListRequest( - Integer term, //조회기간: 1주일, 1개월, 3개월 - Long categoryId, - Long mainCategoryId, - String title, - String nickName, //처리자 닉네임 - TaskStatus taskStatus -) { -} diff --git a/src/main/java/clap/server/adapter/inbound/web/dto/task/OrderRequest.java b/src/main/java/clap/server/adapter/inbound/web/dto/task/OrderRequest.java new file mode 100644 index 00000000..4c6a3e31 --- /dev/null +++ b/src/main/java/clap/server/adapter/inbound/web/dto/task/OrderRequest.java @@ -0,0 +1,11 @@ +package clap.server.adapter.inbound.web.dto.task; + +import io.swagger.v3.oas.annotations.media.Schema; + +public record OrderRequest( + @Schema(description = "정렬 기준 (REQUESTED_AT/FINISHED_AT)", example = "REQUESTED_AT") + String sortBy, + + @Schema(description = "정렬 방향 (ASC/DESC)", example = "ASC") + String sortDirection +) {} diff --git a/src/main/java/clap/server/adapter/inbound/web/dto/task/UpdateTaskRequest.java b/src/main/java/clap/server/adapter/inbound/web/dto/task/UpdateTaskRequest.java index f9dbfeeb..166bdb64 100644 --- a/src/main/java/clap/server/adapter/inbound/web/dto/task/UpdateTaskRequest.java +++ b/src/main/java/clap/server/adapter/inbound/web/dto/task/UpdateTaskRequest.java @@ -1,20 +1,34 @@ package clap.server.adapter.inbound.web.dto.task; +import io.swagger.v3.oas.annotations.media.Schema; import jakarta.validation.constraints.NotBlank; import jakarta.validation.constraints.NotNull; import java.util.List; +@Schema(description = "작업 업데이트 요청") public record UpdateTaskRequest( + + @Schema(description = "작업 ID", example = "123") @NotNull Long taskId, + + @Schema(description = "카테고리 ID", example = "1") @NotNull Long categoryId, + + @Schema(description = "메인 카테고리 ID", example = "10") @NotNull Long mainCategoryId, + + @Schema(description = "작업 제목", example = "업데이트된 제목") @NotBlank String title, + + @Schema(description = "작업 설명", example = "업데이트된 설명.") String description, + + @Schema(description = "첨부 파일 요청 목록", implementation = AttachmentRequest.class) List attachmentRequests -) { -} +) {} + diff --git a/src/main/java/clap/server/adapter/inbound/web/dto/task/UpdateTaskResponse.java b/src/main/java/clap/server/adapter/inbound/web/dto/task/UpdateTaskResponse.java new file mode 100644 index 00000000..9f97ebd8 --- /dev/null +++ b/src/main/java/clap/server/adapter/inbound/web/dto/task/UpdateTaskResponse.java @@ -0,0 +1,8 @@ +package clap.server.adapter.inbound.web.dto.task; + +public record UpdateTaskResponse( + Long taskId, + Long categoryId, + String title +) { +} diff --git a/src/main/java/clap/server/adapter/inbound/web/task/FindTaskController.java b/src/main/java/clap/server/adapter/inbound/web/task/FindTaskController.java index 6ec4bdff..339cb5cd 100644 --- a/src/main/java/clap/server/adapter/inbound/web/task/FindTaskController.java +++ b/src/main/java/clap/server/adapter/inbound/web/task/FindTaskController.java @@ -1,21 +1,28 @@ package clap.server.adapter.inbound.web.task; +import clap.server.adapter.inbound.security.SecurityUserDetails; import clap.server.adapter.inbound.web.dto.task.FindTaskDetailsResponse; -import clap.server.adapter.inbound.web.dto.task.FindTaskListRequest; +import clap.server.adapter.inbound.web.dto.task.FilterTaskListRequest; import clap.server.adapter.inbound.web.dto.task.FindTaskListResponse; import clap.server.application.port.inbound.task.FindTaskDetailsUsecase; import clap.server.application.port.inbound.task.FindTaskListUsecase; import clap.server.common.annotation.architecture.WebAdapter; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; import org.springframework.data.domain.Page; import org.springframework.data.domain.PageRequest; import org.springframework.data.domain.Pageable; import org.springframework.http.ResponseEntity; +import org.springframework.security.access.annotation.Secured; +import org.springframework.security.core.annotation.AuthenticationPrincipal; import org.springframework.web.bind.annotation.*; import java.util.List; +@Tag(name = "작업 조회") @WebAdapter @RestController @RequiredArgsConstructor @@ -23,19 +30,24 @@ public class FindTaskController { private final FindTaskDetailsUsecase taskDetailsUsecase; private final FindTaskListUsecase taskListUsecase; - private static final Long taskId = 3L; - private static final Long memberId = 4L; + + @Operation(summary = "사용자 요청 작업 목록 조회") + @Secured({"ROLE_USER"}) @GetMapping("/requests") public ResponseEntity> getRequestedTaskList( @RequestParam(defaultValue = "0") int page, - @RequestParam(defaultValue = "20") int size, - @RequestBody FindTaskListRequest findTaskListRequest){ - Pageable pageable = PageRequest.of(page, size); - return ResponseEntity.ok(taskListUsecase.findRequestedTaskList(memberId, pageable, findTaskListRequest)); + @RequestParam(defaultValue = "20") int pageSize, + @ModelAttribute FilterTaskListRequest filterTaskListRequest, + @AuthenticationPrincipal SecurityUserDetails userInfo){ + Pageable pageable = PageRequest.of(page, pageSize); + return ResponseEntity.ok(taskListUsecase.findRequestedTaskList(userInfo.getUserId(), pageable, filterTaskListRequest)); } - - @GetMapping("/requests/details") - public ResponseEntity> getRequestedTaskDetails(){ - return ResponseEntity.ok(taskDetailsUsecase.findRequestedTaskDetails(memberId, taskId)); + @Operation(summary = "요청한 작업 상세 조회") + @Secured({"ROLE_USER", "ROLE_MANAGER"}) + @GetMapping("/requests/details/{taskId}") + public ResponseEntity> getRequestedTaskDetails( + @PathVariable Long taskId, + @AuthenticationPrincipal SecurityUserDetails userInfo){ + return ResponseEntity.ok(taskDetailsUsecase.findRequestedTaskDetails(userInfo.getUserId(), taskId)); } } \ No newline at end of file diff --git a/src/main/java/clap/server/adapter/inbound/web/task/ManagementTaskController.java b/src/main/java/clap/server/adapter/inbound/web/task/ManagementTaskController.java index ff9b1151..a87f30bb 100644 --- a/src/main/java/clap/server/adapter/inbound/web/task/ManagementTaskController.java +++ b/src/main/java/clap/server/adapter/inbound/web/task/ManagementTaskController.java @@ -1,17 +1,23 @@ package clap.server.adapter.inbound.web.task; +import clap.server.adapter.inbound.security.SecurityUserDetails; import clap.server.adapter.inbound.web.dto.task.CreateTaskRequest; -import clap.server.adapter.inbound.web.dto.task.CreateAndUpdateTaskResponse; +import clap.server.adapter.inbound.web.dto.task.CreateTaskResponse; import clap.server.adapter.inbound.web.dto.task.UpdateTaskRequest; +import clap.server.adapter.inbound.web.dto.task.UpdateTaskResponse; import clap.server.application.port.inbound.task.CreateTaskUsecase; import clap.server.application.port.inbound.task.UpdateTaskUsecase; import clap.server.common.annotation.architecture.WebAdapter; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; import jakarta.validation.Valid; import lombok.RequiredArgsConstructor; import org.springframework.http.ResponseEntity; +import org.springframework.security.core.annotation.AuthenticationPrincipal; import org.springframework.web.bind.annotation.*; +@Tag(name = "작업 생성 및 수정") @WebAdapter @RestController @RequiredArgsConstructor @@ -20,17 +26,20 @@ public class ManagementTaskController { private final CreateTaskUsecase createTaskUsecase; private final UpdateTaskUsecase updateTaskUsecase; - private static final Long memberId = 4L; + @Operation(summary = "작업 요청 생성") @PostMapping - public ResponseEntity createTask( - @RequestBody @Valid CreateTaskRequest createTaskRequest){ - return ResponseEntity.ok(createTaskUsecase.createTask(memberId, createTaskRequest)); + public ResponseEntity createTask( + @RequestBody @Valid CreateTaskRequest createTaskRequest, + @AuthenticationPrincipal SecurityUserDetails userInfo){ + return ResponseEntity.ok(createTaskUsecase.createTask(userInfo.getUserId(), createTaskRequest)); } + @Operation(summary = "요청한 작업 수정") @PatchMapping - public ResponseEntity updateTask( - @RequestBody @Valid UpdateTaskRequest updateTaskRequest){ - return ResponseEntity.ok(updateTaskUsecase.updateTask(memberId, updateTaskRequest)); + public ResponseEntity updateTask( + @RequestBody @Valid UpdateTaskRequest updateTaskRequest, + @AuthenticationPrincipal SecurityUserDetails userInfo){ + return ResponseEntity.ok(updateTaskUsecase.updateTask(userInfo.getUserId(), updateTaskRequest)); } } diff --git a/src/main/java/clap/server/adapter/outbound/persistense/AttachmentPersistenceAdapter.java b/src/main/java/clap/server/adapter/outbound/persistense/AttachmentPersistenceAdapter.java index 63af74cc..6a08cc30 100644 --- a/src/main/java/clap/server/adapter/outbound/persistense/AttachmentPersistenceAdapter.java +++ b/src/main/java/clap/server/adapter/outbound/persistense/AttachmentPersistenceAdapter.java @@ -38,8 +38,8 @@ public void saveAll(List attachments) { } @Override - public List findAllByTaskId(final Long taskId) { - List attachmentEntities = attachmentRepository.findAllByTask_TaskId(taskId); + public List findAllByTaskIdAndCommentIsNull(final Long taskId) { + List attachmentEntities = attachmentRepository.findAllByTask_TaskIdAndCommentIsNull(taskId); return attachmentEntities.stream() .map(attachmentPersistenceMapper::toDomain) .collect(Collectors.toList()); 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 79bfb48f..c8128d32 100644 --- a/src/main/java/clap/server/adapter/outbound/persistense/TaskPersistenceAdapter.java +++ b/src/main/java/clap/server/adapter/outbound/persistense/TaskPersistenceAdapter.java @@ -1,6 +1,6 @@ package clap.server.adapter.outbound.persistense; -import clap.server.adapter.inbound.web.dto.task.FindTaskListRequest; +import clap.server.adapter.inbound.web.dto.task.FilterTaskListRequest; import clap.server.adapter.inbound.web.dto.task.FindTaskListResponse; import clap.server.adapter.outbound.persistense.entity.task.TaskEntity; import clap.server.adapter.outbound.persistense.mapper.TaskPersistenceMapper; @@ -38,7 +38,7 @@ public Optional findById(Long id) { } @Override - public Page findAllByRequesterId(Long requesterId, Pageable pageable, FindTaskListRequest findTaskListRequest) { + public Page findAllByRequesterId(Long requesterId, Pageable pageable, FilterTaskListRequest findTaskListRequest) { Page taskList = taskRepository.findRequestedTaskList(requesterId, pageable, findTaskListRequest) .map(taskPersistenceMapper::toDomain); return taskList.map(TaskMapper::toFindTaskListResponse); diff --git a/src/main/java/clap/server/adapter/outbound/persistense/entity/task/CommentEntity.java b/src/main/java/clap/server/adapter/outbound/persistense/entity/task/CommentEntity.java index 43e3d39a..6654f4ec 100644 --- a/src/main/java/clap/server/adapter/outbound/persistense/entity/task/CommentEntity.java +++ b/src/main/java/clap/server/adapter/outbound/persistense/entity/task/CommentEntity.java @@ -27,7 +27,7 @@ public class CommentEntity extends BaseTimeEntity { @JoinColumn(name = "task_id", nullable = false) private TaskEntity task; - @Column(name = "content", nullable = false) + @Column(name = "content", nullable = false) //nullable false인 이유 private String content; @Column(name = "is_modified", nullable = false) diff --git a/src/main/java/clap/server/adapter/outbound/persistense/entity/task/constant/TaskStatus.java b/src/main/java/clap/server/adapter/outbound/persistense/entity/task/constant/TaskStatus.java index 011eb8e6..2bb4f027 100644 --- a/src/main/java/clap/server/adapter/outbound/persistense/entity/task/constant/TaskStatus.java +++ b/src/main/java/clap/server/adapter/outbound/persistense/entity/task/constant/TaskStatus.java @@ -10,7 +10,7 @@ public enum TaskStatus { REQUESTED("요청"), IN_PROGRESS("진행 중"), - PENDING_COMPLETED("검토중"), + PENDING_COMPLETED("검토 중"), COMPLETED("완료"), TERMINATED("종료"); 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 ce18103a..08387c40 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,8 +4,8 @@ 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/AttachmentRepository.java b/src/main/java/clap/server/adapter/outbound/persistense/repository/task/AttachmentRepository.java index c3b71706..fd7db8be 100644 --- a/src/main/java/clap/server/adapter/outbound/persistense/repository/task/AttachmentRepository.java +++ b/src/main/java/clap/server/adapter/outbound/persistense/repository/task/AttachmentRepository.java @@ -7,8 +7,7 @@ @Repository public interface AttachmentRepository extends JpaRepository { - List findAllByTask_TaskId(Long taskId); - // fileIds 목록을 받아 해당하는 파일들을 삭제 + List findAllByTask_TaskIdAndCommentIsNull(Long taskId); void deleteAllByAttachmentIdIn(List attachmentIds); } \ No newline at end of file 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 be6dee0e..dd7028c5 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 @@ -1,11 +1,11 @@ package clap.server.adapter.outbound.persistense.repository.task; -import clap.server.adapter.inbound.web.dto.task.FindTaskListRequest; +import clap.server.adapter.inbound.web.dto.task.FilterTaskListRequest; import clap.server.adapter.outbound.persistense.entity.task.TaskEntity; import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; public interface TaskCustomRepository { - Page findRequestedTaskList(Long requesterId, Pageable pageable, FindTaskListRequest findTaskListRequest); + Page findRequestedTaskList(Long requesterId, Pageable pageable, FilterTaskListRequest findTaskListRequest); } 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 c7336c87..be184f6f 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,10 +1,11 @@ package clap.server.adapter.outbound.persistense.repository.task; -import clap.server.adapter.inbound.web.dto.task.FindTaskListRequest; -import clap.server.adapter.outbound.persistense.entity.task.QTaskEntity; +import clap.server.adapter.inbound.web.dto.task.FilterTaskListRequest; import clap.server.adapter.outbound.persistense.entity.task.TaskEntity; import clap.server.adapter.outbound.persistense.entity.task.constant.TaskStatus; import com.querydsl.core.BooleanBuilder; +import com.querydsl.core.types.OrderSpecifier; +import com.querydsl.core.types.dsl.DateTimePath; import com.querydsl.jpa.impl.JPAQueryFactory; import lombok.RequiredArgsConstructor; import org.springframework.data.domain.Page; @@ -15,6 +16,9 @@ import java.time.LocalDateTime; import java.util.List; +import static clap.server.adapter.outbound.persistense.entity.task.QTaskEntity.taskEntity; +import static com.querydsl.core.types.Order.*; + @Repository @RequiredArgsConstructor public class TaskCustomRepositoryImpl implements TaskCustomRepository { @@ -22,50 +26,63 @@ public class TaskCustomRepositoryImpl implements TaskCustomRepository { private final JPAQueryFactory queryFactory; @Override - public Page findRequestedTaskList(Long requesterId, Pageable pageable, FindTaskListRequest findTaskListRequest) { - QTaskEntity task = QTaskEntity.taskEntity; + public Page findRequestedTaskList(Long requesterId, Pageable pageable, FilterTaskListRequest findTaskListRequest) { BooleanBuilder whereClause = new BooleanBuilder(); - whereClause.and(task.requester.memberId.eq(requesterId)); + whereClause.and(taskEntity.requester.memberId.eq(requesterId)); - Long categoryId = findTaskListRequest.categoryId(); - Long mainCategoryId = findTaskListRequest.mainCategoryId(); + List categoryIds = findTaskListRequest.categoryIds(); + List mainCategoryIds = findTaskListRequest.mainCategoryIds(); String title = findTaskListRequest.title(); String nickName = findTaskListRequest.nickName(); - TaskStatus taskStatus = findTaskListRequest.taskStatus(); - Integer term = findTaskListRequest.term(); + List taskStatuses = findTaskListRequest.taskStatus(); + Integer termHours = findTaskListRequest.term(); + String sortBy = findTaskListRequest.orderRequest().sortBy(); + String sortDirection = findTaskListRequest.orderRequest().sortDirection(); - if (term != null) { - LocalDateTime fromDate = LocalDateTime.now().minusMonths(term); - whereClause.and(task.createdAt.after(fromDate)); + if (termHours != null) { + LocalDateTime fromDate = LocalDateTime.now().minusHours(termHours); + whereClause.and(taskEntity.createdAt.after(fromDate)); } - if (categoryId != null) { - whereClause.and(task.category.categoryId.eq(categoryId)); + if (!categoryIds.isEmpty()) { + whereClause.and(taskEntity.category.categoryId.in(categoryIds)); } - if (mainCategoryId != null) { - whereClause.and(task.category.mainCategory.categoryId.eq(mainCategoryId)); + if (!mainCategoryIds.isEmpty()) { + whereClause.and(taskEntity.category.mainCategory.categoryId.in(mainCategoryIds)); } - if (title != null && !title.isEmpty()) { - whereClause.and(task.title.containsIgnoreCase(title)); + if (!title.isEmpty()) { + whereClause.and(taskEntity.title.containsIgnoreCase(title)); } - if (nickName != null) { - whereClause.and(task.processor.nickname.eq(nickName)); + if (!nickName.isEmpty()) { + whereClause.and(taskEntity.processor.nickname.eq(nickName)); } - if (taskStatus != null) { - whereClause.and(task.taskStatus.eq(taskStatus)); + if (!taskStatuses.isEmpty()) { + whereClause.and(taskEntity.taskStatus.in(taskStatuses)); } + OrderSpecifier orderSpecifier = getOrderSpecifier(sortBy, sortDirection); + List result = queryFactory - .selectFrom(task) + .selectFrom(taskEntity) .where(whereClause) + .orderBy(orderSpecifier) .offset(pageable.getOffset()) .limit(pageable.getPageSize()) .fetch(); - int total = queryFactory - .selectFrom(task) + .selectFrom(taskEntity) .where(whereClause) .fetch().size(); - return new PageImpl<>(result, pageable, total); } + + private OrderSpecifier getOrderSpecifier(String sortBy, String sortDirection) { + DateTimePath sortColumn = switch (sortBy) { + case "REQUESTED_AT" -> taskEntity.updatedAt; + case "FINISHED_AT" -> taskEntity.completedAt; + default -> taskEntity.updatedAt; + }; + return "ASC".equalsIgnoreCase(sortDirection) + ? new OrderSpecifier<>(ASC, sortColumn) + : new OrderSpecifier<>(DESC, sortColumn); + } } 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 46bd8bd2..c4fdc318 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,22 +1,19 @@ package clap.server.adapter.outbound.persistense.repository.task; -import clap.server.adapter.inbound.web.dto.task.FindTaskListRequest; + 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.Pageable; + import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.Query; import org.springframework.stereotype.Repository; -import java.nio.channels.FileChannel; + import java.time.LocalDateTime; import java.util.Collection; -import org.springframework.stereotype.Repository; - -import java.time.LocalDateTime; import java.util.List; diff --git a/src/main/java/clap/server/application/Task/CreateTaskService.java b/src/main/java/clap/server/application/Task/CreateTaskService.java index c490d067..7fb52efa 100644 --- a/src/main/java/clap/server/application/Task/CreateTaskService.java +++ b/src/main/java/clap/server/application/Task/CreateTaskService.java @@ -1,7 +1,7 @@ package clap.server.application.Task; import clap.server.adapter.inbound.web.dto.task.CreateTaskRequest; -import clap.server.adapter.inbound.web.dto.task.CreateAndUpdateTaskResponse; +import clap.server.adapter.inbound.web.dto.task.CreateTaskResponse; import clap.server.application.mapper.TaskMapper; import clap.server.application.port.inbound.domain.CategoryService; @@ -33,7 +33,7 @@ public class CreateTaskService implements CreateTaskUsecase { @Override @Transactional - public CreateAndUpdateTaskResponse createTask(Long requesterId, CreateTaskRequest createTaskRequest) { + public CreateTaskResponse createTask(Long requesterId, CreateTaskRequest createTaskRequest) { Member member = memberService.findActiveMember(requesterId); Category category = categoryService.findById(createTaskRequest.categoryId()); Task task = Task.createTask(member, category, createTaskRequest.title(), createTaskRequest.description()); @@ -42,6 +42,6 @@ public CreateAndUpdateTaskResponse createTask(Long requesterId, CreateTaskReques List attachments = Attachment.createAttachments(savedTask, createTaskRequest.fileUrls()); commandAttachmentPort.saveAll(attachments); - return TaskMapper.toCreateAndUpdateTaskResponse(savedTask); + return TaskMapper.toCreateTaskResponse(savedTask); } } \ No newline at end of file diff --git a/src/main/java/clap/server/application/Task/FindTaskDetailsService.java b/src/main/java/clap/server/application/Task/FindTaskDetailsService.java index c9ed511f..c48dc7b9 100644 --- a/src/main/java/clap/server/application/Task/FindTaskDetailsService.java +++ b/src/main/java/clap/server/application/Task/FindTaskDetailsService.java @@ -32,7 +32,7 @@ public List findRequestedTaskDetails(final Long request memberService.findActiveMember(requesterId); Task task = loadTaskPort.findById(taskId) .orElseThrow(()-> new ApplicationException(TaskErrorCode.TASK_NOT_FOUND)); - List attachments = loadAttachmentPort.findAllByTaskId(taskId); //TODO: comment에 달린 이미지 첨부파일은 가져오지 않도록 수정 + List attachments = loadAttachmentPort.findAllByTaskIdAndCommentIsNull(taskId); return TaskMapper.toFindTaskDetailResponses(task, attachments); } } diff --git a/src/main/java/clap/server/application/Task/FindTaskListService.java b/src/main/java/clap/server/application/Task/FindTaskListService.java index 52c1af1f..bb8d9183 100644 --- a/src/main/java/clap/server/application/Task/FindTaskListService.java +++ b/src/main/java/clap/server/application/Task/FindTaskListService.java @@ -1,6 +1,6 @@ package clap.server.application.Task; -import clap.server.adapter.inbound.web.dto.task.FindTaskListRequest; +import clap.server.adapter.inbound.web.dto.task.FilterTaskListRequest; import clap.server.adapter.inbound.web.dto.task.FindTaskListResponse; import clap.server.application.port.inbound.domain.MemberService; @@ -28,7 +28,7 @@ public class FindTaskListService implements FindTaskListUsecase { @Override - public Page findRequestedTaskList(Long requesterId, Pageable pageable, FindTaskListRequest findTaskListRequest) { + public Page findRequestedTaskList(Long requesterId, Pageable pageable, FilterTaskListRequest findTaskListRequest) { Member requester = memberService.findActiveMember(requesterId); return loadTaskPort.findAllByRequesterId(requester.getMemberId(), pageable, findTaskListRequest); } diff --git a/src/main/java/clap/server/application/Task/UpdateTaskService.java b/src/main/java/clap/server/application/Task/UpdateTaskService.java index 7fb5a7ae..e83d1b7f 100644 --- a/src/main/java/clap/server/application/Task/UpdateTaskService.java +++ b/src/main/java/clap/server/application/Task/UpdateTaskService.java @@ -1,7 +1,7 @@ package clap.server.application.Task; -import clap.server.adapter.inbound.web.dto.task.CreateAndUpdateTaskResponse; import clap.server.adapter.inbound.web.dto.task.UpdateTaskRequest; +import clap.server.adapter.inbound.web.dto.task.UpdateTaskResponse; import clap.server.application.mapper.AttachmentMapper; import clap.server.application.mapper.TaskMapper; import clap.server.application.port.inbound.domain.CategoryService; @@ -39,19 +39,19 @@ public class UpdateTaskService implements UpdateTaskUsecase { @Override @Transactional - public CreateAndUpdateTaskResponse updateTask(Long requesterId, UpdateTaskRequest updateTaskRequest) { - Member member = memberService.findActiveMember(requesterId); + public UpdateTaskResponse updateTask(Long requesterId, UpdateTaskRequest updateTaskRequest) { + memberService.findActiveMember(requesterId); Category category = categoryService.findById(updateTaskRequest.categoryId()); Task task = taskService.findById(updateTaskRequest.taskId()); - - Task updatedTask = Task.updateTask(task, member, category, updateTaskRequest.title(), updateTaskRequest.description()); - Task savedTask = commandTaskPort.save(updatedTask); + //TODO: 작업이 요청 상태인 경우만 업데이트 가능 + task.updateTask(category, updateTaskRequest.title(), updateTaskRequest.description()); + Task updatedTask = commandTaskPort.save(task); List attachmentIds = AttachmentMapper.toAttachmentIds(updateTaskRequest.attachmentRequests()); commandAttachmentPort.deleteByIds(attachmentIds); - List attachments = Attachment.updateAttachments(savedTask, updateTaskRequest.attachmentRequests()); + List attachments = Attachment.updateAttachments(updatedTask, updateTaskRequest.attachmentRequests()); commandAttachmentPort.saveAll(attachments); - return TaskMapper.toCreateAndUpdateTaskResponse(savedTask); + return TaskMapper.toUpdateTaskResponse(updatedTask); } } diff --git a/src/main/java/clap/server/application/mapper/AttachmentMapper.java b/src/main/java/clap/server/application/mapper/AttachmentMapper.java index 73713949..9387bbad 100644 --- a/src/main/java/clap/server/application/mapper/AttachmentMapper.java +++ b/src/main/java/clap/server/application/mapper/AttachmentMapper.java @@ -15,8 +15,6 @@ private AttachmentMapper() { throw new IllegalArgumentException(); } - - public static List toAttachmentIds(List attachmentRequests) { return attachmentRequests.stream() .map(AttachmentRequest::fileId) diff --git a/src/main/java/clap/server/application/mapper/TaskMapper.java b/src/main/java/clap/server/application/mapper/TaskMapper.java index eab5dad0..752c8833 100644 --- a/src/main/java/clap/server/application/mapper/TaskMapper.java +++ b/src/main/java/clap/server/application/mapper/TaskMapper.java @@ -1,18 +1,14 @@ package clap.server.application.mapper; -import clap.server.adapter.inbound.web.dto.task.AttachmentResponse; -import clap.server.adapter.inbound.web.dto.task.CreateAndUpdateTaskResponse; -import clap.server.adapter.inbound.web.dto.task.FindTaskDetailsResponse; -import clap.server.adapter.inbound.web.dto.task.FindTaskListResponse; -import clap.server.adapter.outbound.persistense.entity.task.constant.TaskStatus; -import clap.server.domain.model.member.Member; +import clap.server.adapter.inbound.web.dto.task.*; + import clap.server.domain.model.task.Attachment; -import clap.server.domain.model.task.Category; + import clap.server.domain.model.task.Task; +import lombok.extern.slf4j.Slf4j; import java.time.LocalDateTime; -import java.time.format.DateTimeFormatter; import java.util.List; import java.util.stream.Collectors; @@ -20,37 +16,25 @@ public class TaskMapper { private TaskMapper() { throw new IllegalArgumentException(); } - private static final String formattedDateTime = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyMMddHHmm")); - - public static Task toUpdatedTask(Task task, Member member, Category category, String title, String description) { - - return Task.builder() - .taskId(task.getTaskId()) - .title(title) - .description(description) - .category(category) - .requester(member) - .taskStatus(TaskStatus.REQUESTED) - .taskCode(category.getMainCategory().getCode() + formattedDateTime) - .build(); + public static CreateTaskResponse toCreateTaskResponse(Task task) { + return new CreateTaskResponse(task.getTaskId(), task.getCategory().getCategoryId(), task.getTitle()); } - - public static CreateAndUpdateTaskResponse toCreateAndUpdateTaskResponse(Task task) { - return new CreateAndUpdateTaskResponse(task.getTaskId(), task.getCategory().getCategoryId(), task.getTitle()); + public static UpdateTaskResponse toUpdateTaskResponse(Task task) { + return new UpdateTaskResponse(task.getTaskId(), task.getCategory().getCategoryId(), task.getTitle()); } public static FindTaskListResponse toFindTaskListResponse(Task task) { return new FindTaskListResponse( task.getTaskId(), task.getTaskCode(), - task.getCreatedAt(), + task.getUpdatedAt(), task.getCategory().getMainCategory().getName(), task.getCategory().getName(), task.getTitle(), - task.getProcessor() != null ? task.getProcessor().getMemberInfo().getName() : null, + task.getProcessor() != null ? task.getProcessor().getMemberInfo().getNickname() : "", task.getTaskStatus(), - task.getCompletedAt() + task.getCompletedAt() != null ? task.getCompletedAt() : null ); } @@ -74,8 +58,8 @@ public static List toFindTaskDetailResponses(Task task, task.getTaskStatus(), task.getRequester().getMemberInfo().getNickname(), task.getRequester().getImageUrl(), - task.getProcessor() != null ? task.getProcessor().getMemberInfo().getNickname() : null, - task.getProcessor() != null ? task.getProcessor().getImageUrl() : null, + task.getProcessor() != null ? task.getProcessor().getMemberInfo().getNickname() : "", + task.getProcessor() != null ? task.getProcessor().getImageUrl() : "", task.getCategory().getMainCategory().getName(), task.getCategory().getName(), task.getTitle(), diff --git a/src/main/java/clap/server/application/port/inbound/task/CreateTaskUsecase.java b/src/main/java/clap/server/application/port/inbound/task/CreateTaskUsecase.java index 1627b9f4..9529e066 100644 --- a/src/main/java/clap/server/application/port/inbound/task/CreateTaskUsecase.java +++ b/src/main/java/clap/server/application/port/inbound/task/CreateTaskUsecase.java @@ -1,8 +1,8 @@ package clap.server.application.port.inbound.task; import clap.server.adapter.inbound.web.dto.task.CreateTaskRequest; -import clap.server.adapter.inbound.web.dto.task.CreateAndUpdateTaskResponse; +import clap.server.adapter.inbound.web.dto.task.CreateTaskResponse; public interface CreateTaskUsecase { - CreateAndUpdateTaskResponse createTask(Long memberId, CreateTaskRequest createTaskRequest); + CreateTaskResponse createTask(Long memberId, CreateTaskRequest createTaskRequest); } diff --git a/src/main/java/clap/server/application/port/inbound/task/FindTaskListUsecase.java b/src/main/java/clap/server/application/port/inbound/task/FindTaskListUsecase.java index ccb56170..8d85fdbf 100644 --- a/src/main/java/clap/server/application/port/inbound/task/FindTaskListUsecase.java +++ b/src/main/java/clap/server/application/port/inbound/task/FindTaskListUsecase.java @@ -1,10 +1,10 @@ package clap.server.application.port.inbound.task; -import clap.server.adapter.inbound.web.dto.task.FindTaskListRequest; +import clap.server.adapter.inbound.web.dto.task.FilterTaskListRequest; import clap.server.adapter.inbound.web.dto.task.FindTaskListResponse; import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; public interface FindTaskListUsecase { - Page findRequestedTaskList(Long memberId, Pageable pageable, FindTaskListRequest findTaskListRequest); + Page findRequestedTaskList(Long memberId, Pageable pageable, FilterTaskListRequest findTaskListRequest); } diff --git a/src/main/java/clap/server/application/port/inbound/task/UpdateTaskUsecase.java b/src/main/java/clap/server/application/port/inbound/task/UpdateTaskUsecase.java index caf1c41f..18d27670 100644 --- a/src/main/java/clap/server/application/port/inbound/task/UpdateTaskUsecase.java +++ b/src/main/java/clap/server/application/port/inbound/task/UpdateTaskUsecase.java @@ -1,9 +1,9 @@ package clap.server.application.port.inbound.task; -import clap.server.adapter.inbound.web.dto.task.CreateAndUpdateTaskResponse; import clap.server.adapter.inbound.web.dto.task.UpdateTaskRequest; +import clap.server.adapter.inbound.web.dto.task.UpdateTaskResponse; public interface UpdateTaskUsecase { - CreateAndUpdateTaskResponse updateTask(Long memberId, UpdateTaskRequest updateTaskRequest); + UpdateTaskResponse updateTask(Long memberId, UpdateTaskRequest updateTaskRequest); } diff --git a/src/main/java/clap/server/application/port/outbound/task/LoadAttachmentPort.java b/src/main/java/clap/server/application/port/outbound/task/LoadAttachmentPort.java index bd8d8df3..6796877d 100644 --- a/src/main/java/clap/server/application/port/outbound/task/LoadAttachmentPort.java +++ b/src/main/java/clap/server/application/port/outbound/task/LoadAttachmentPort.java @@ -6,5 +6,5 @@ public interface LoadAttachmentPort { - List findAllByTaskId(Long task); + List findAllByTaskIdAndCommentIsNull(Long task); } 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 4419aee5..9e8cb996 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 @@ -1,6 +1,6 @@ package clap.server.application.port.outbound.task; -import clap.server.adapter.inbound.web.dto.task.FindTaskListRequest; +import clap.server.adapter.inbound.web.dto.task.FilterTaskListRequest; import clap.server.adapter.inbound.web.dto.task.FindTaskListResponse; import clap.server.domain.model.task.Task; import org.springframework.data.domain.Page; @@ -15,6 +15,6 @@ public interface LoadTaskPort { List findYesterdayTaskByDate(LocalDateTime now); - Page findAllByRequesterId(Long requesterId, Pageable pageable, FindTaskListRequest findTaskListRequest); + Page findAllByRequesterId(Long requesterId, Pageable pageable, FilterTaskListRequest findTaskListRequest); } \ No newline at end of file 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 4289c6ae..d6be02bc 100644 --- a/src/main/java/clap/server/domain/model/task/Task.java +++ b/src/main/java/clap/server/domain/model/task/Task.java @@ -23,7 +23,7 @@ public class Task extends BaseTime { private Category category; private Member requester; private TaskStatus taskStatus; - private int processorOrder; //칸반보드 상태에 따른 순서 + private int processorOrder; private Member processor; private Label label; private Member reviewer; @@ -37,21 +37,18 @@ public static Task createTask(Member member, Category category, String title, St .title(title) .description(description) .taskStatus(TaskStatus.REQUESTED) - .taskCode(category.getMainCategory().getCode() + LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyMMddHHmm"))) + .taskCode(toTaskCode(category)) .build(); } - public static Task updateTask(Task task, Member member, Category category, String title, String description) { - - return Task.builder() - .taskId(task.getTaskId()) - .requester(member) - .category(category) - .title(title) - .description(description) - .taskStatus(TaskStatus.REQUESTED) - .taskCode(category.getMainCategory().getCode() + LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyMMddHHmm"))) - .build(); + public void updateTask(Category category, String title, String description) { + this.category = category; + this.title = title; + this.description = description; + this.taskCode = toTaskCode(category); } + private static String toTaskCode(Category category){ + return category.getMainCategory().getCode() + LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyMMddHHmm")); + } } diff --git a/src/main/java/clap/server/exception/code/TaskErrorCode.java b/src/main/java/clap/server/exception/code/TaskErrorCode.java index 7e29befd..189d1d61 100644 --- a/src/main/java/clap/server/exception/code/TaskErrorCode.java +++ b/src/main/java/clap/server/exception/code/TaskErrorCode.java @@ -9,6 +9,7 @@ public enum TaskErrorCode implements BaseErrorCode { TASK_NOT_FOUND(HttpStatus.NOT_FOUND, "TASK_001", "작업을 찾을 수 없습니다."), CATEGORY_NOT_FOUND(HttpStatus.NOT_FOUND, "TASK_002", "카테고리를 찾을 수 없습니다."), + TASK_STATUS_MISMATCH(HttpStatus.BAD_REQUEST, "TASK_003", "작업 상태가 일치하지 않습니다."); ; private final HttpStatus httpStatus; diff --git a/src/main/resources/redis.yml b/src/main/resources/redis.yml index c1157e75..9016361b 100644 --- a/src/main/resources/redis.yml +++ b/src/main/resources/redis.yml @@ -1,6 +1,8 @@ spring: data: redis: - host: ${REDIS_HOST:localhost} + host: ${REDIS_HOST:127.0.0.1} port: ${REDIS_PORT:6379} - password: ${REDIS_PASSWORD} \ No newline at end of file + password: ${REDIS_PASSWORD} + +