Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,7 @@

public record SliceResponse<T> (
List<T> content,
int currentPage,
int size,
boolean hasNext,
boolean isFirst,
boolean isLast
) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package clap.server.adapter.inbound.web.dto.task.response;

import java.util.List;

public record TaskBoardResponse(
List<TaskItemResponse> tasksInProgress,
List<TaskItemResponse> tasksPendingComplete,
List<TaskItemResponse> tasksCompleted,
boolean hasNext,
boolean isFirst,
boolean isLast
){
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
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 TaskItemResponse(
Long taskId,
String taskCode,
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 @@ -37,6 +37,7 @@ public ResponseEntity<Page<FilterTaskListResponse>> findTasksRequestedByUser(
Pageable pageable = PageRequest.of(page, pageSize);
return ResponseEntity.ok(taskListUsecase.findTasksRequestedByUser(userInfo.getUserId(), pageable, filterTaskListRequest));
}

@Operation(summary = "요청한 작업 상세 조회")
@Secured({"ROLE_USER", "ROLE_MANAGER"})
@GetMapping("/requests/details/{taskId}")
Expand All @@ -45,7 +46,8 @@ public ResponseEntity<FindTaskDetailsResponse> findRequestedTaskDetails(
@AuthenticationPrincipal SecurityUserDetails userInfo){
return ResponseEntity.ok(taskDetailsUsecase.findRequestedTaskDetails(userInfo.getUserId(), taskId));
}
@Operation(summary = "승인대기 중인 요청 목록 조회")

@Operation(summary = "승인 대기 중인 요청 목록 조회")
@Secured({"ROLE_MANAGER"})
@GetMapping("/requests/pending")
public ResponseEntity<Page<FilterPendingApprovalResponse>> findPendingApprovalTasks(
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
package clap.server.adapter.inbound.web.task;

import clap.server.adapter.inbound.security.SecurityUserDetails;
import clap.server.adapter.inbound.web.dto.task.response.TaskBoardResponse;
import clap.server.application.port.inbound.task.TaskBoardUsecase;
import clap.server.common.annotation.architecture.WebAdapter;
import io.swagger.v3.oas.annotations.Parameter;
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.format.annotation.DateTimeFormat;
import org.springframework.http.ResponseEntity;
import org.springframework.security.core.annotation.AuthenticationPrincipal;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

import java.time.LocalDate;

@Tag(name = "작업 보드 조회 및 순서 변경")
@WebAdapter
@RestController
@RequiredArgsConstructor
@RequestMapping("/api/task-board")
public class TaskBoardController {
private final TaskBoardUsecase taskBoardUsecase;

@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,
@AuthenticationPrincipal SecurityUserDetails userInfo) {
Pageable pageable = PageRequest.of(page, pageSize);
return ResponseEntity.ok(taskBoardUsecase.getTaskBoards(userInfo.getUserId(), untilDate, pageable));
}


}
Original file line number Diff line number Diff line change
Expand Up @@ -39,21 +39,16 @@ public void saveAll(List<Attachment> attachments) {

@Override
public List<Attachment> findAllByTaskIdAndCommentIsNull(final Long taskId) {
List<AttachmentEntity> attachmentEntities = attachmentRepository.findAllByTask_TaskIdAndCommentIsNull(taskId);
List<AttachmentEntity> attachmentEntities = attachmentRepository.findAllByTask_TaskIdAndCommentIsNullAndIsDeletedIsFalse(taskId);
return attachmentEntities.stream()
.map(attachmentPersistenceMapper::toDomain)
.collect(Collectors.toList());
}

public List<Attachment> findAllByTaskIdAndCommentIsNullAndAttachmentId(final Long taskId, final List<Long> attachmentIds) {
List<AttachmentEntity> attachmentEntities = attachmentRepository.findAllByTask_TaskIdAndCommentIsNullAndAttachmentIdIn(taskId, attachmentIds);
List<AttachmentEntity> attachmentEntities = attachmentRepository.findAllByTask_TaskIdAndCommentIsNullAndIsDeletedIsFalseAndAttachmentIdIn(taskId, attachmentIds);
return attachmentEntities.stream()
.map(attachmentPersistenceMapper::toDomain)
.collect(Collectors.toList());
}

@Override
public void deleteByIds(List<Long> attachmentIds) {
attachmentRepository.deleteAllByAttachmentIdIn(attachmentIds);
}
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
package clap.server.adapter.outbound.persistense.repository;
package clap.server.adapter.outbound.persistense;

import clap.server.adapter.outbound.persistense.entity.task.LabelEntity;
import clap.server.adapter.outbound.persistense.entity.task.TaskEntity;
import clap.server.adapter.outbound.persistense.mapper.LabelPersistenceMapper;
import clap.server.adapter.outbound.persistense.repository.task.LabelRepository;
import clap.server.application.port.outbound.task.LoadLabelPort;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import clap.server.adapter.inbound.web.dto.task.FilterTaskListRequest;
import clap.server.adapter.inbound.web.dto.task.FilterTaskListResponse;
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;
import clap.server.adapter.outbound.persistense.repository.task.TaskRepository;
import clap.server.application.mapper.TaskMapper;
Expand All @@ -12,19 +13,22 @@
import clap.server.common.annotation.architecture.PersistenceAdapter;
import clap.server.domain.model.task.Task;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
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;
import java.util.Optional;


@Slf4j
@PersistenceAdapter
@RequiredArgsConstructor
public class TaskPersistenceAdapter implements CommandTaskPort , LoadTaskPort {
private final TaskRepository taskRepository;
private final TaskPersistenceMapper taskPersistenceMapper;

@Override
public Task save(Task task) {
TaskEntity taskEntity = taskPersistenceMapper.toEntity(task);
Expand Down Expand Up @@ -52,6 +56,13 @@ public Page<FilterPendingApprovalResponse> findPendingApprovalTasks(Pageable pag
return taskList.map(TaskMapper::toFilterPendingApprovalTasksResponse);
}

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

@Override
public List<Task> findYesterdayTaskByDate(LocalDateTime now) {
return taskRepository.findYesterdayTaskByUpdatedAtIsBetween(now.minusDays(1), now)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,4 +34,7 @@ public class AttachmentEntity extends BaseTimeEntity {
@OneToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "comment_id")
private CommentEntity comment;

@Column(nullable = false)
private boolean isDeleted;
}
Original file line number Diff line number Diff line change
Expand Up @@ -42,8 +42,8 @@ public class TaskEntity extends BaseTimeEntity {
@Enumerated(EnumType.STRING)
private TaskStatus taskStatus;

@Column(nullable = false)
private int processorOrder;
@Column
private Long processorOrder;

@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "reviewer_id")
Expand All @@ -62,4 +62,7 @@ public class TaskEntity extends BaseTimeEntity {

@Column
private LocalDateTime completedAt;

@Column
private LocalDateTime finishedAt;
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
public enum TaskStatus {
REQUESTED("요청"),
IN_PROGRESS("진행 중"),
PENDING_COMPLETED("검토 중"),
PENDING_COMPLETED("완료 대기"),
COMPLETED("완료"),
TERMINATED("종료");

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,16 @@
import clap.server.adapter.outbound.persistense.mapper.common.PersistenceMapper;
import clap.server.domain.model.task.Attachment;
import org.mapstruct.Mapper;
import org.mapstruct.Mapping;

@Mapper(componentModel = "spring", uses = {TaskPersistenceMapper.class, CommentPersistenceMapper.class})
public interface AttachmentPersistenceMapper extends PersistenceMapper<AttachmentEntity, Attachment> {
@Override
@Mapping(source = "deleted", target = "isDeleted")
Attachment toDomain(final AttachmentEntity attachment);

@Override
@Mapping(source = "deleted", target = "isDeleted")
AttachmentEntity toEntity(final Attachment Attachment);

}
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,11 @@
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;

import java.util.Collection;
import java.util.List;

@Repository
public interface AttachmentRepository extends JpaRepository<AttachmentEntity, Long> {
List<AttachmentEntity> findAllByTask_TaskIdAndCommentIsNull(Long taskId);
void deleteAllByAttachmentIdIn(List<Long> attachmentIds);
List<AttachmentEntity> findAllByTask_TaskIdAndCommentIsNullAndAttachmentIdIn(Long task_taskId, List<Long> attachmentId);
List<AttachmentEntity> findAllByTask_TaskIdAndCommentIsNullAndIsDeletedIsFalse(Long taskId);
List<AttachmentEntity> findAllByTask_TaskIdAndCommentIsNullAndIsDeletedIsFalseAndAttachmentIdIn(Long task_taskId, List<Long> attachmentId);

}
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,10 @@


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.domain.Slice;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import org.springframework.stereotype.Repository;
Expand All @@ -20,6 +23,19 @@ List<TaskEntity> findYesterdayTaskByUpdatedAtIsBetween(
@Param("updatedAtAfter") LocalDateTime updatedAtAfter,
@Param("updatedAtBefore") LocalDateTime updatedAtBefore
);


@Query("SELECT t FROM TaskEntity t WHERE t.processor.memberId = :processorId " +
"AND t.taskStatus IN :taskStatus " +
"AND (t.taskStatus != 'COMPLETED' OR t.completedAt >= :untilDate)")
Slice<TaskEntity> findTasksWithTaskStatusAndCompletedAt(
@Param("processorId") Long processorId,
@Param("taskStatus") List<TaskStatus> taskStatus,
@Param("untilDate") LocalDateTime untilDate,
Pageable pageable
);


}


Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,8 @@ public CreateTaskResponse createTask(Long requesterId, CreateTaskRequest createT
Category category = categoryService.findById(createTaskRequest.categoryId());
Task task = Task.createTask(member, category, createTaskRequest.title(), createTaskRequest.description());
Task savedTask = commandTaskPort.save(task);
savedTask.setInitialProcessorOrder();
commandTaskPort.save(savedTask);

saveAttachments(files, savedTask);
publishNotification(savedTask);
Expand Down
38 changes: 38 additions & 0 deletions src/main/java/clap/server/application/Task/TaskBoardService.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
package clap.server.application.Task;

import clap.server.adapter.inbound.web.dto.task.response.TaskBoardResponse;
import clap.server.adapter.outbound.persistense.entity.task.constant.TaskStatus;
import clap.server.application.mapper.TaskMapper;
import clap.server.application.port.inbound.domain.MemberService;
import clap.server.application.port.inbound.task.TaskBoardUsecase;
import clap.server.application.port.outbound.task.LoadTaskPort;
import clap.server.common.annotation.architecture.ApplicationService;
import clap.server.domain.model.task.Task;
import lombok.RequiredArgsConstructor;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Slice;
import org.springframework.transaction.annotation.Transactional;

import java.time.LocalDate;
import java.util.List;

@ApplicationService
@RequiredArgsConstructor
class TaskBoardService implements TaskBoardUsecase {
private final static List<TaskStatus> VIEWABLE_STATUSES = List.of(
TaskStatus.IN_PROGRESS,
TaskStatus.PENDING_COMPLETED,
TaskStatus.COMPLETED
);
private final MemberService memberService;
private final LoadTaskPort loadTaskPort;

@Transactional(readOnly = true)
@Override
public TaskBoardResponse getTaskBoards(Long processorId, LocalDate untilDate, Pageable pageable) {
memberService.findById(processorId);
Slice<Task> tasks = loadTaskPort.findByProcessorAndStatus(processorId, VIEWABLE_STATUSES, untilDate.plusDays(1).atStartOfDay(), pageable);
return TaskMapper.toSliceTaskItemResponse(tasks);
}

}
17 changes: 10 additions & 7 deletions src/main/java/clap/server/application/Task/UpdateTaskService.java
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@
import org.springframework.web.multipart.MultipartFile;

import java.util.List;

import java.util.Objects;


@ApplicationService
Expand All @@ -47,13 +47,15 @@ public class UpdateTaskService implements UpdateTaskUsecase {
@Override
@Transactional
public UpdateTaskResponse updateTask(Long requesterId, Long taskId, UpdateTaskRequest updateTaskRequest, List<MultipartFile> files) {
memberService.findActiveMember(requesterId);
Member requester = memberService.findActiveMember(requesterId);
Category category = categoryService.findById(updateTaskRequest.categoryId());
Task task = taskService.findById(taskId);
if(task.getTaskStatus() != TaskStatus.REQUESTED){

if(!Objects.equals(requester.getMemberId(), task.getRequester().getMemberId())) {
throw new ApplicationException(TaskErrorCode.TASK_STATUS_MISMATCH);
}
task.updateTask(category, updateTaskRequest.title(), updateTaskRequest.description());

task.updateTask(task.getTaskStatus(), category, updateTaskRequest.title(), updateTaskRequest.description());
Task updatedTask = commandTaskPort.save(task);

if (!updateTaskRequest.attachmentsToDelete().isEmpty()){
Expand All @@ -63,18 +65,19 @@ public UpdateTaskResponse updateTask(Long requesterId, Long taskId, UpdateTaskRe
}

private void updateAttachments(List<Long> attachmentIdsToDelete, List<MultipartFile> files, Task task) {
validateAttachments(attachmentIdsToDelete, task);
commandAttachmentPort.deleteByIds(attachmentIdsToDelete);
List<Attachment> attachmentsToDelete = validateAndGetAttachments(attachmentIdsToDelete, task);
attachmentsToDelete.forEach(Attachment::softDelete);

List<String> fileUrls = s3UploadAdapter.uploadFiles(FilePath.TASK_IMAGE, files);
List<Attachment> attachments = AttachmentMapper.toTaskAttachments(task, files, fileUrls);
commandAttachmentPort.saveAll(attachments);
}

private void validateAttachments(List<Long> attachmentIdsToDelete, Task task) {
private List<Attachment> validateAndGetAttachments(List<Long> attachmentIdsToDelete, Task task) {
List<Attachment> attachmentsOfTask = loadAttachmentPort.findAllByTaskIdAndCommentIsNullAndAttachmentId(task.getTaskId(), attachmentIdsToDelete);
if(attachmentsOfTask.size() != attachmentIdsToDelete.size()) {
throw new ApplicationException(TaskErrorCode.TASK_ATTACHMENT_NOT_FOUND);
}
return attachmentsOfTask;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,7 @@ public static FindNotificationListResponse toFindNoticeListResponse(Notification
public static SliceResponse<FindNotificationListResponse> toSliceOfFindNoticeListResponse(Slice<FindNotificationListResponse> slice) {
return new SliceResponse<>(
slice.getContent(),
slice.getNumber(),
slice.getSize(),
slice.hasNext(),
slice.isFirst(),
slice.isLast()
);
Expand Down
Loading
Loading