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

Feat: POST 저장 기능, OCR 구현, 폴더 수정, 페이지별 조회 기능 구현 및 배포 #67

Merged
merged 1 commit into from
Oct 25, 2024
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
5 changes: 5 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
FROM gradle:jdk21-jammy

WORKDIR /app

CMD ["gradle", "bootRun"]
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ public class KakaoOauthClient implements OauthClient {

@Override
public Member fetchMember(String accessToken) {
return kakaoClient.fetchMember(accessToken).toDomain();
return kakaoClient.fetchMember("Bearer " + accessToken).toDomain();
}

@Override
Expand Down
15 changes: 10 additions & 5 deletions src/main/java/notai/common/config/AuthInterceptor.java
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,12 @@
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import notai.auth.TokenService;
import notai.common.exception.ErrorMessages;
import notai.common.exception.type.UnAuthorizedException;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;

import static notai.common.exception.ErrorMessages.*;
import static org.springframework.http.HttpHeaders.AUTHORIZATION;

@Component
Expand All @@ -22,14 +25,16 @@ public AuthInterceptor(TokenService tokenService) {
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
String header = request.getHeader(AUTHORIZATION);
if (header == null || !header.startsWith(AUTHENTICATION_TYPE)) {
response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
return false;
throw new UnAuthorizedException(NOTFOUND_ACCESS_TOKEN);
}

String token = header.substring(BEARER_PREFIX_LENGTH);
Long memberId = tokenService.extractMemberId(token);
request.setAttribute("memberId", memberId);

try {
Long memberId = tokenService.extractMemberId(token);
request.setAttribute("memberId", memberId);
} catch (Exception e) {
throw new UnAuthorizedException(INVALID_ACCESS_TOKEN);
}
return true;
}
}
25 changes: 9 additions & 16 deletions src/main/java/notai/common/exception/ErrorMessages.java
Original file line number Diff line number Diff line change
Expand Up @@ -9,18 +9,16 @@ public enum ErrorMessages {
ANNOTATION_NOT_FOUND("주석을 찾을 수 없습니다."),

// document
DOCUMENT_NOT_FOUND("자료를 찾을 수 없습니다."),
DOCUMENT_NOT_FOUND("자료를 찾을 수 없습니다."), INVALID_DOCUMENT_PAGE("존재하지 않는 페이지 입니다."),

// ocr
OCR_RESULT_NOT_FOUND("OCR 데이터를 찾을 수 없습니다."),
OCR_TASK_ERROR("PDF 파일을 통해 OCR 작업을 수행하는데 실패했습니다."),
OCR_RESULT_NOT_FOUND("OCR 데이터를 찾을 수 없습니다."), OCR_TASK_ERROR("PDF 파일을 통해 OCR 작업을 수행하는데 실패했습니다."),

// folder
FOLDER_NOT_FOUND("폴더를 찾을 수 없습니다."),

// llm task
LLM_TASK_LOG_NOT_FOUND("AI 작업 기록을 찾을 수 없습니다."),
LLM_TASK_RESULT_ERROR("AI 요약 및 문제 생성 중에 문제가 발생했습니다."),
LLM_TASK_LOG_NOT_FOUND("AI 작업 기록을 찾을 수 없습니다."), LLM_TASK_RESULT_ERROR("AI 요약 및 문제 생성 중에 문제가 발생했습니다."),

// problem
PROBLEM_NOT_FOUND("문제 정보를 찾을 수 없습니다."),
Expand All @@ -35,24 +33,19 @@ public enum ErrorMessages {
RECORDING_NOT_FOUND("녹음 파일을 찾을 수 없습니다."),

// external api call
KAKAO_API_ERROR("카카오 API 호출에 예외가 발생했습니다."),
AI_SERVER_ERROR("AI 서버 API 호출에 예외가 발생했습니다."),
KAKAO_API_ERROR("카카오 API 호출에 예외가 발생했습니다."), AI_SERVER_ERROR("AI 서버 API 호출에 예외가 발생했습니다."),

// auth
INVALID_ACCESS_TOKEN("유효하지 않은 토큰입니다."),
INVALID_REFRESH_TOKEN("유요하지 않은 Refresh Token입니다."),
EXPIRED_REFRESH_TOKEN("만료된 Refresh Token입니다."),
INVALID_LOGIN_TYPE("지원하지 않는 소셜 로그인 타입입니다."),
INVALID_ACCESS_TOKEN("유효하지 않은 토큰입니다."), INVALID_REFRESH_TOKEN("유요하지 않은 Refresh Token입니다."), EXPIRED_REFRESH_TOKEN(
"만료된 Refresh Token입니다."), INVALID_LOGIN_TYPE("지원하지 않는 소셜 로그인 타입입니다."), NOTFOUND_ACCESS_TOKEN(
"토큰 정보가 존재하지 않습니다."),

// json conversion
JSON_CONVERSION_ERROR("JSON-객체 변환 중에 오류가 발생했습니다."),

// etc
INVALID_FILE_TYPE("지원하지 않는 파일 형식입니다."),
FILE_NOT_FOUND("존재하지 않는 파일입니다."),
FILE_SAVE_ERROR("파일을 저장하는 과정에서 오류가 발생했습니다."),
INVALID_AUDIO_ENCODING("오디오 파일이 잘못되었습니다.")
;
INVALID_FILE_TYPE("지원하지 않는 파일 형식입니다."), FILE_NOT_FOUND("존재하지 않는 파일입니다."), FILE_SAVE_ERROR(
"파일을 저장하는 과정에서 오류가 발생했습니다."), INVALID_AUDIO_ENCODING("오디오 파일이 잘못되었습니다.");

private final String message;

Expand Down
29 changes: 22 additions & 7 deletions src/main/java/notai/document/application/DocumentService.java
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;

import java.util.List;

@Service
@RequiredArgsConstructor
public class DocumentService {
Expand All @@ -28,7 +30,7 @@ public DocumentSaveResult saveDocument(
Long folderId, MultipartFile pdfFile, DocumentSaveRequest documentSaveRequest
) {
PdfSaveResult pdfSaveResult = pdfService.savePdf(pdfFile);
Document document = saveAndReturnDocument(folderId, documentSaveRequest, pdfSaveResult.pdfUrl());
Document document = saveAndReturnDocument(folderId, documentSaveRequest, pdfSaveResult);
ocrService.saveOCR(document, pdfSaveResult.pdf());
return DocumentSaveResult.of(document.getId(), document.getName(), document.getUrl());
}
Expand All @@ -37,7 +39,7 @@ public DocumentSaveResult saveRootDocument(
MultipartFile pdfFile, DocumentSaveRequest documentSaveRequest
) {
PdfSaveResult pdfSaveResult = pdfService.savePdf(pdfFile);
Document document = saveAndReturnRootDocument(documentSaveRequest, pdfSaveResult.pdfUrl());
Document document = saveAndReturnRootDocument(documentSaveRequest, pdfSaveResult);
ocrService.saveOCR(document, pdfSaveResult.pdf());
return DocumentSaveResult.of(document.getId(), document.getName(), document.getUrl());
}
Expand All @@ -57,23 +59,36 @@ public void deleteDocument(
) {
Document document = documentRepository.getById(documentId);
document.validateDocument(folderId);
ocrService.deleteAllByDocument(document);
documentRepository.delete(document);
}

public void deleteAllByFolder(
Folder folder
) {
documentRepository.deleteAllByFolder(folder);
List<Document> documents = documentRepository.findAllByFolderId(folder.getId());
for (Document document : documents) {
deleteDocument(folder.getId(), document.getId());
}
}

private Document saveAndReturnDocument(Long folderId, DocumentSaveRequest documentSaveRequest, String pdfUrl) {
private Document saveAndReturnDocument(
Long folderId, DocumentSaveRequest documentSaveRequest, PdfSaveResult pdfSaveResult
) {
Folder folder = folderRepository.getById(folderId);
Document document = new Document(folder, documentSaveRequest.name(), pdfUrl);
Document document = new Document(folder,
documentSaveRequest.name(),
pdfSaveResult.pdfUrl(),
pdfSaveResult.totalPages()
);
return documentRepository.save(document);
}

private Document saveAndReturnRootDocument(DocumentSaveRequest documentSaveRequest, String pdfUrl) {
Document document = new Document(documentSaveRequest.name(), pdfUrl);
private Document saveAndReturnRootDocument(DocumentSaveRequest documentSaveRequest, PdfSaveResult pdfSaveResult) {
Document document = new Document(documentSaveRequest.name(),
pdfSaveResult.pdfUrl(),
pdfSaveResult.totalPages()
);
return documentRepository.save(document);
}
}
47 changes: 0 additions & 47 deletions src/main/java/notai/document/application/PdfService.java

This file was deleted.

25 changes: 18 additions & 7 deletions src/main/java/notai/document/domain/Document.java
Original file line number Diff line number Diff line change
@@ -1,18 +1,18 @@
package notai.document.domain;

import jakarta.persistence.*;
import static jakarta.persistence.GenerationType.IDENTITY;
import jakarta.validation.constraints.NotNull;
import static lombok.AccessLevel.PROTECTED;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import notai.common.domain.RootEntity;
import static notai.common.exception.ErrorMessages.DOCUMENT_NOT_FOUND;
import static notai.common.exception.ErrorMessages.INVALID_DOCUMENT_PAGE;
import notai.common.exception.type.NotFoundException;
import notai.folder.domain.Folder;

import static jakarta.persistence.GenerationType.IDENTITY;
import static lombok.AccessLevel.PROTECTED;
import static notai.common.exception.ErrorMessages.DOCUMENT_NOT_FOUND;

@Slf4j
@Entity
@Table(name = "document")
Expand All @@ -36,24 +36,35 @@ public class Document extends RootEntity<Long> {
@Column(name = "url")
private String url;

public Document(Folder folder, String name, String url) {
@NotNull
@Column(name = "total_pages")
private Integer totalPages;

public Document(Folder folder, String name, String url, Integer totalPages) {
this.folder = folder;
this.name = name;
this.url = url;
this.totalPages = totalPages;
}

public Document(String name, String url) {
public Document(String name, String url, Integer totalPages) {
this.name = name;
this.url = url;
this.totalPages = totalPages;
}

public void validateDocument(Long folderId) {
if (!this.folder.getId().equals(folderId)) {
log.info("요청 폴더와 실제 문서를 소유한 폴더가 다릅니다.");
throw new NotFoundException(DOCUMENT_NOT_FOUND);
}
}

public void validatePageNumber(Integer pageNumber) {
if (totalPages < pageNumber) {
throw new NotFoundException(INVALID_DOCUMENT_PAGE);
}
}

public void updateName(String name) {
this.name = name;
}
Expand Down
6 changes: 1 addition & 5 deletions src/main/java/notai/document/domain/DocumentRepository.java
Original file line number Diff line number Diff line change
@@ -1,20 +1,16 @@
package notai.document.domain;

import static notai.common.exception.ErrorMessages.DOCUMENT_NOT_FOUND;
import notai.common.exception.type.NotFoundException;
import notai.document.query.DocumentQueryRepository;
import notai.folder.domain.Folder;
import org.springframework.data.jpa.repository.JpaRepository;

import java.util.List;

import static notai.common.exception.ErrorMessages.DOCUMENT_NOT_FOUND;

public interface DocumentRepository extends JpaRepository<Document, Long>, DocumentQueryRepository {
default Document getById(Long id) {
return findById(id).orElseThrow(() -> new NotFoundException(DOCUMENT_NOT_FOUND));
}

List<Document> findAllByFolderId(Long folderId);

void deleteAllByFolder(Folder folder);
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,16 +11,14 @@
import notai.document.presentation.response.DocumentFindResponse;
import notai.document.presentation.response.DocumentSaveResponse;
import notai.document.presentation.response.DocumentUpdateResponse;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;

import java.net.URI;
import java.util.List;

@Controller
@RestController
@RequestMapping("/api/folders/{folderId}/documents")
@RequiredArgsConstructor
public class DocumentController {
Expand All @@ -31,7 +29,7 @@ public class DocumentController {
private static final Long ROOT_FOLDER_ID = -1L;
private static final String FOLDER_URL_FORMAT = "/api/folders/%s/documents/%s";

@PostMapping(consumes = {MediaType.MULTIPART_FORM_DATA_VALUE})
@PostMapping
public ResponseEntity<DocumentSaveResponse> saveDocument(
@PathVariable Long folderId,
@RequestPart MultipartFile pdfFile,
Expand Down
30 changes: 0 additions & 30 deletions src/main/java/notai/document/presentation/PdfController.java

This file was deleted.

Loading