Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[FIX] 키워드 조회 API 로직 수정 #103

Merged
merged 8 commits into from
Aug 17, 2023
26 changes: 8 additions & 18 deletions src/main/java/site/katchup/katchupserver/api/card/domain/Card.java
Original file line number Diff line number Diff line change
@@ -1,13 +1,12 @@
package site.katchup.katchupserver.api.card.domain;

import jakarta.persistence.*;
import lombok.AccessLevel;
import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;
import site.katchup.katchupserver.api.link.domain.Link;
import site.katchup.katchupserver.api.screenshot.domain.Screenshot;
import site.katchup.katchupserver.api.task.domain.Task;
import site.katchup.katchupserver.api.subTask.domain.SubTask;
import site.katchup.katchupserver.common.domain.BaseEntity;

import java.time.LocalDateTime;
Expand Down Expand Up @@ -37,14 +36,14 @@ public class Card extends BaseEntity {
private String note;

@Column(name = "is_deleted", nullable = false)
private boolean isDeleted;
private Boolean isDeleted;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

p4;

Wrapper class로 바꾼 이유가 궁금합니다! 기본적으로 Entity boolean값은 원시 타입으로 해두는 것이 좋다고 생각하는데 이유가 있을까요?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

어라,,,? 뭐지 바꾼 기억이 없는데,,,ㅎ 다시 바꿔 놓겠습니다 !


@Column(name = "deleted_at")
private LocalDateTime deletedAt;

@ManyToOne(fetch = LAZY)
@JoinColumn(name = "task_id")
private Task task;
@JoinColumn(name = "sub_task_id")
private SubTask subTask;

@OneToMany(mappedBy = "card", cascade = ALL)
private List<Screenshot> screenshots = new ArrayList<>();
Expand All @@ -56,22 +55,13 @@ public class Card extends BaseEntity {
private List<Link> links = new ArrayList<>();

@Builder
public Card(Long placementOrder, String content, String note, Task task) {
public Card(Long placementOrder, String content, String note, SubTask subTask) {
this.placementOrder = placementOrder;
this.content = content;
this.isDeleted = false;
this.note = note;
this.task = task;
}

@Builder
public Card(Long placementOrder, String content, String note, boolean isDeleted, LocalDateTime deletedAt, Task task) {
this.placementOrder = placementOrder;
this.content = content;
this.note = note;
this.isDeleted = isDeleted;
this.deletedAt = deletedAt;
this.task = task;
this.task.addCard(this);
this.subTask = subTask;
this.subTask.addCard(this);
}

public void addScreenshot(Screenshot screenshot) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,16 +9,16 @@
@AllArgsConstructor(staticName = "of")
@Getter
public class CardCreateRequestDto {
@NotBlank(message = "대분류는 필수 값입니다.")
@NotBlank(message = "CD-110")
private Long categoryId;
@NotBlank(message = "중분류는 필수 값입니다.")
private Long folderId;
@NotBlank(message = "소분류는 필수 값입니다.")
@NotBlank(message = "CD-111")
private Long taskId;
@NotBlank(message = "키워드는 필수 값입니다.")
@NotBlank(message = "CD-112")
private Long subTaskId;
@NotBlank(message = "CD-113")
private List<Long> keywordIdList;
private String note;
@NotBlank(message = "내용은 필수 값입니다.")
@NotBlank(message = "CD-114")
private String content;

}
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,8 @@
public class CardGetResponseDto {
private Long cardId;
private String category;
private String folder;
private String task;
private String subTask;
private List<KeywordGetResponseDto> keywordList;
private List<ScreenshotGetResponseDto> screenshotList;
private List<FileGetResponseDto> fileList;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
import lombok.*;
import site.katchup.katchupserver.api.card.domain.Card;
import site.katchup.katchupserver.api.keyword.dto.response.KeywordGetResponseDto;
import site.katchup.katchupserver.api.task.domain.Task;
import site.katchup.katchupserver.api.subTask.domain.SubTask;

import java.util.ArrayList;
import java.util.List;
Expand All @@ -15,21 +15,21 @@
@AllArgsConstructor
@Builder
public class CardListGetResponseDto {
private Long taskId;
private Long subTaskId;
private Long cardId;
private Long placementOrder;
private String cardName;
private String subTaskName;
private List<KeywordGetResponseDto> keywordList = new ArrayList<>();
private String content;
private Boolean existFile;

public static CardListGetResponseDto of (Card card, Task task, List<KeywordGetResponseDto> keywordList) {
public static CardListGetResponseDto of (Card card, SubTask subTask, List<KeywordGetResponseDto> keywordList) {
Boolean existFile = card.getFiles().isEmpty() ? false : true;
return CardListGetResponseDto.builder()
.cardId(card.getId())
.taskId(task.getId())
.subTaskId(subTask.getId())
.placementOrder(card.getPlacementOrder())
.cardName(task.getName())
.subTaskName(subTask.getName())
.keywordList(keywordList)
.content(card.getContent())
.existFile(existFile)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,13 @@

@Repository
public interface CardRepository extends JpaRepository<Card, Long> {
List<Card> findByTaskId(Long taskId);

default Card findByIdOrThrow(Long cardId) {
Card card = findById(cardId).orElseThrow(
() -> new NotFoundException(ErrorCode.NOT_FOUND_CARD)
);

if (card.isDeleted()) {
if (card.getIsDeleted()) {
throw new NotFoundException(ErrorCode.DELETED_CARD);
}
return card;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,13 +17,14 @@
import site.katchup.katchupserver.api.card.service.CardService;
import site.katchup.katchupserver.api.category.domain.Category;
import site.katchup.katchupserver.api.category.repository.CategoryRepository;
import site.katchup.katchupserver.api.common.CardProvider;
import site.katchup.katchupserver.api.folder.domain.Folder;
import site.katchup.katchupserver.api.folder.repository.FolderRepository;
import site.katchup.katchupserver.api.keyword.domain.CardKeyword;
import site.katchup.katchupserver.api.keyword.dto.response.KeywordGetResponseDto;
import site.katchup.katchupserver.api.keyword.repository.CardKeywordRepository;
import site.katchup.katchupserver.api.keyword.repository.KeywordRepository;
import site.katchup.katchupserver.api.screenshot.dto.response.ScreenshotGetResponseDto;
import site.katchup.katchupserver.api.screenshot.repository.ScreenshotRepository;
import site.katchup.katchupserver.api.subTask.domain.SubTask;
import site.katchup.katchupserver.api.subTask.repository.SubTaskRepository;
import site.katchup.katchupserver.api.task.domain.Task;
import site.katchup.katchupserver.api.task.repository.TaskRepository;
import site.katchup.katchupserver.api.trash.domain.Trash;
Expand All @@ -43,15 +44,15 @@
@RequiredArgsConstructor
public class CardServiceImpl implements CardService {

private final TaskRepository taskRepository;
private final SubTaskRepository subTaskRepository;
private final CardKeywordRepository cardKeywordRepository;
private final TrashRepository trashRepository;
private final CardProvider cardProvider;
private final CardRepository cardRepository;
private final CategoryRepository categoryRepository;
private final FolderRepository folderRepository;
private final TaskRepository taskRepository;
private final FileRepository fileRepository;
private final ScreenshotRepository screenshotRepository;
private final KeywordRepository keywordRepository;
private final S3Util s3Util;

private static final String FILE_FOLDER_NAME = "files/";
Expand All @@ -61,19 +62,19 @@ public class CardServiceImpl implements CardService {

@Override
@Transactional
public List<CardListGetResponseDto> getCardList(Long folderId) {
return taskRepository.findByFolderId(folderId).stream()
.flatMap(task -> task.getCards().stream())
.collect(Collectors.groupingBy(Card::getTask)) // taskId로 그룹화
public List<CardListGetResponseDto> getCardList(Long taskId) {
return subTaskRepository.findByTaskId(taskId).stream()
.flatMap(subTask -> subTask.getCards().stream())
.collect(Collectors.groupingBy(Card::getSubTask)) // subTaskId 그룹화
.values().stream()
.flatMap(cards -> cards.stream().sorted(Comparator.comparing(Card::getPlacementOrder))) // 그룹 내에서 placementOrder 값으로 정렬
.filter(card -> !card.isDeleted()) // isDeleted가 false인 card 필터링
.filter(card -> !card.getIsDeleted()) // isDeleted가 false인 card 필터링
.map(card -> {
List<KeywordGetResponseDto> keywords = cardKeywordRepository.findByCardId(card.getId()).stream()
.map(cardKeyword -> KeywordGetResponseDto.of(cardKeyword.getKeyword()))
.collect(Collectors.toList());
Task relatedTask = card.getTask(); // Card와 연관된 Task 가져오기
return CardListGetResponseDto.of(card, relatedTask, keywords);
SubTask relatedSubTask = card.getSubTask(); // Card와 연관된 SubTask 가져오기
return CardListGetResponseDto.of(card, relatedSubTask, keywords);
})
.collect(Collectors.toList());
}
Expand All @@ -82,7 +83,7 @@ public List<CardListGetResponseDto> getCardList(Long folderId) {
@Transactional
public void deleteCardList(CardDeleteRequestDto cardDeleteRequestDto) {
cardDeleteRequestDto.getCardIdList().stream()
.map(cardProvider::getCardById)
.map(cardRepository::findByIdOrThrow)
.forEach(findCard -> {
findCard.deletedCard();
trashRepository.save(Trash.builder().card(findCard).build());
Expand All @@ -93,16 +94,23 @@ public void deleteCardList(CardDeleteRequestDto cardDeleteRequestDto) {
@Transactional
public void createCard(List<MultipartFile> fileList, CardCreateRequestDto requestDto) {

Task task = taskRepository.findByIdOrThrow(requestDto.getTaskId());
SubTask subTask = subTaskRepository.findByIdOrThrow(requestDto.getTaskId());

Card card = Card.builder()
.content(requestDto.getContent())
.note(requestDto.getNote())
.placementOrder(getPlacementOrder(task))
.task(task)
.placementOrder(getPlacementOrder(subTask))
.subTask(subTask)
.build();

cardRepository.save(card);
Card savedCard = cardRepository.save(card);

for (Long keywordId : requestDto.getKeywordIdList()) {
CardKeyword.builder()
.card(savedCard)
.keyword(keywordRepository.findByIdOrThrow(keywordId))
.build();
}

try {
for (MultipartFile file : fileList) {
Expand All @@ -127,35 +135,35 @@ public void createCard(List<MultipartFile> fileList, CardCreateRequestDto reques
@Override
public CardGetResponseDto getCard(Long cardId) {
Card card = cardRepository.findByIdOrThrow(cardId);
Folder folder = folderRepository.findByIdOrThrow(card.getTask().getFolder().getId());
Category category = categoryRepository.findByIdOrThrow(folder.getCategory().getId());
Task task = taskRepository.findByIdOrThrow(card.getSubTask().getTask().getId());
Category category = categoryRepository.findByIdOrThrow(task.getCategory().getId());

List<KeywordGetResponseDto> keywordResponseDtoList = getKeywordDtoList(cardId);
List<FileGetResponseDto> fileGetResponseDTOList = getFileDtoList(cardId);
List<ScreenshotGetResponseDto> screenshotResponseDtoList = getScreenshotDtoList(cardId);

return CardGetResponseDto.of(card.getId(), category.getName(), folder.getName(), card.getTask().getName(), keywordResponseDtoList, screenshotResponseDtoList, fileGetResponseDTOList);
return CardGetResponseDto.of(card.getId(), category.getName(), task.getName(), card.getSubTask().getName(), keywordResponseDtoList, screenshotResponseDtoList, fileGetResponseDTOList);
}

private Long getPlacementOrder(Task task) {
if (task.getCards().size() == 0) {
Folder folder = folderRepository.findByIdOrThrow(task.getFolder().getId());
List<Task> taskList = folder.getTasks().stream().sorted(Comparator.comparing(Task::getName)).collect(Collectors.toList());
private Long getPlacementOrder(SubTask subTask) {
if (subTask.getCards().size() == 0) {
Task task = taskRepository.findByIdOrThrow(subTask.getTask().getId());
List<SubTask> subTaskList = task.getSubTasks().stream().sorted(Comparator.comparing(SubTask::getName)).collect(Collectors.toList());
Long placementOrder = 0L;

for (Task sortedTask: taskList) {
placementOrder += sortedTask.getCards().size();
if (sortedTask.getId().equals(task.getId())) {
for (SubTask sortedSubTask: subTaskList) {
placementOrder += sortedSubTask.getCards().size();
if (sortedSubTask.getId().equals(subTask.getId())) {
placementOrder += 1;
break;
}
}
return placementOrder;
// task 위치에서 1번째 카드 생성
// subTask 위치에서 1번째 카드 생성
} else {
// task가 갖고 있는 카드 중에서 마지막 카드의 바로 아래 생성
Folder folder = folderRepository.findByIdOrThrow(task.getFolder().getId());
List<Card> cardList = task.getCards().stream().collect(Collectors.toList());
// subTask가 갖고 있는 카드 중에서 마지막 카드의 바로 아래 생성
Task task = taskRepository.findByIdOrThrow(subTask.getTask().getId());
List<Card> cardList = subTask.getCards().stream().collect(Collectors.toList());
Card maxPlacmentOrderCard = cardList.stream().max(Comparator.comparing(Card::getPlacementOrder)).get();
Long maxPlacementOrder = maxPlacmentOrderCard.getPlacementOrder();
Long placementOrder = maxPlacementOrder + 1;
Expand All @@ -179,7 +187,8 @@ private void validateFileSize(MultipartFile file) {
private List<KeywordGetResponseDto> getKeywordDtoList(Long cardId) {
return cardKeywordRepository.findByCardId(cardId)
.stream()
.map(cardKeyword -> KeywordGetResponseDto.of(cardKeyword.getKeyword().getId(), cardKeyword.getKeyword().getName()))
.map(cardKeyword -> KeywordGetResponseDto.of(cardKeyword.getKeyword().getId(),
cardKeyword.getKeyword().getName(), cardKeyword.getKeyword().getColor()))
.collect(Collectors.toList());
}
private List<ScreenshotGetResponseDto> getScreenshotDtoList(Long cardId) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,17 +23,17 @@
@RestController
@RequiredArgsConstructor
@RequestMapping("/api/v1/categories")
@Tag(name = "[Category] 대분류 관련 API (V1)")
@Tag(name = "[Category] 카테고리 관련 API (V1)")
public class CategoryController {

private final CategoryService categoryService;

@Operation(summary = "대분류 생성 API")
@Operation(summary = "카테고리 생성 API")
@PostMapping()
@ResponseStatus(HttpStatus.CREATED)
@ApiResponses(value = {
@ApiResponse(responseCode = "201", description = "대분류 생성 성공"),
@ApiResponse(responseCode = "400", description = "대분류 생성 실패", content = @Content),
@ApiResponse(responseCode = "201", description = "카테고리 생성 성공"),
@ApiResponse(responseCode = "400", description = "카테고리 생성 실패", content = @Content),
@ApiResponse(responseCode = "500", description = "서버 오류", content = @Content)
})
public ApiResponseDto createCategoryName(Principal principal,
Expand All @@ -43,10 +43,10 @@ public ApiResponseDto createCategoryName(Principal principal,
return ApiResponseDto.success();
}

@Operation(summary = "대분류 조회 API")
@Operation(summary = "카테고리 조회 API")
@ApiResponses(value = {
@ApiResponse(responseCode = "200", description = "대분류 조회 성공"),
@ApiResponse(responseCode = "400", description = "대분류 조회 실패", content = @Content),
@ApiResponse(responseCode = "200", description = "카테고리 조회 성공"),
@ApiResponse(responseCode = "400", description = "카테고리 조회 실패", content = @Content),
@ApiResponse(responseCode = "500", description = "서버 오류", content = @Content)
})
@GetMapping()
Expand All @@ -56,12 +56,12 @@ public ApiResponseDto<List<CategoryGetResponseDto>> getAllCategory(Principal pri
return ApiResponseDto.success(categoryService.getAllCategory(memberId));
}

@Operation(summary = "대분류 이름 수정 API")
@Operation(summary = "카테고리 이름 수정 API")
@PatchMapping("/{categoryId}")
@ResponseStatus(HttpStatus.OK)
@ApiResponses(value = {
@ApiResponse(responseCode = "200", description = "대분류 이름 수정 성공"),
@ApiResponse(responseCode = "400", description = "대분류 이름 수정 실패", content = @Content),
@ApiResponse(responseCode = "200", description = "카테고리 이름 수정 성공"),
@ApiResponse(responseCode = "400", description = "카테고리 이름 수정 실패", content = @Content),
@ApiResponse(responseCode = "500", description = "서버 오류", content = @Content)
})
public ApiResponseDto updateCategoryName(Principal principal, @PathVariable final Long categoryId,
Expand All @@ -71,10 +71,10 @@ public ApiResponseDto updateCategoryName(Principal principal, @PathVariable fina
return ApiResponseDto.success();
}

@Operation(summary = "대분류 삭제 API")
@Operation(summary = "카테고리 삭제 API")
@ApiResponses(value = {
@ApiResponse(responseCode = "200", description = "대분류 삭제 성공"),
@ApiResponse(responseCode = "400", description = "대분류 삭제 실패", content = @Content),
@ApiResponse(responseCode = "200", description = "카테고리 삭제 성공"),
@ApiResponse(responseCode = "400", description = "카테고리 삭제 실패", content = @Content),
@ApiResponse(responseCode = "500", description = "서버 오류", content = @Content)
})
@DeleteMapping("/{categoryId}")
Expand Down
Loading
Loading