Skip to content

Commit

Permalink
Feat: Annotaion API, Recording API, Document API 구현 (#50)
Browse files Browse the repository at this point in the history
* Style: formatting통일 (#30)

* Feat: 요약 및 문제 생성 API 구현 (#32)

* Rename: AI Task 도메인 이름 변경

- llm 으로 변경

* Refactor: LLM 도메인 애플리케이션 계층과 프레젠테이션 계층 응답 분리

* Feat: LLM 작업 진행 상태 확인 기능 구현

* Feat: 요약 및 문제 결과 조회 기능 구현

* Feat: 요약 및 문제 생성 기능 구현

- AI 서버와 통신하는 부분 제외하고 기능 구현
- 임시 UUID 를 통해 task 저장

* Feat: LLM 서버 콜백 기능 구현

- LLM 서버가 API 콜을 통해 페이지별 요약 및 문제 내용 전달
- task id 를 통해 조회하여 요약 내용과 문제 내용 업데이트

* Refactor: 변수 이름 변경

- 필드명 카멜케이스로 변경

* Refactor: 통일성 없는 부분 수정

- 필드명 변경
- 변수 추출

* Refactor: 예외 종류, 메서드 네이밍 변경

- LLMQueryService 예외 타입 변경
- SummaryAndProblemUpdateResponse 메서드 네이밍 변경

* Refactor: LLMQueryService 응답과 LLMController 응답 분리

* Style: 코드 포맷팅 통일 (#36)

* Feat: 요약 및 문제 생성 API 구현 (#4)

* Rename: AI Task 도메인 이름 변경

- llm 으로 변경

* Refactor: LLM 도메인 애플리케이션 계층과 프레젠테이션 계층 응답 분리

* Feat: LLM 작업 진행 상태 확인 기능 구현

* Feat: 요약 및 문제 결과 조회 기능 구현

* Feat: 요약 및 문제 생성 기능 구현

- AI 서버와 통신하는 부분 제외하고 기능 구현
- 임시 UUID 를 통해 task 저장

* Feat: LLM 서버 콜백 기능 구현

- LLM 서버가 API 콜을 통해 페이지별 요약 및 문제 내용 전달
- task id 를 통해 조회하여 요약 내용과 문제 내용 업데이트

* Refactor: 변수 이름 변경

- 필드명 카멜케이스로 변경

* Refactor: 통일성 없는 부분 수정

- 필드명 변경
- 변수 추출

* Refactor: 예외 종류, 메서드 네이밍 변경

- LLMQueryService 예외 타입 변경
- SummaryAndProblemUpdateResponse 메서드 네이밍 변경

* Refactor: LLMQueryService 응답과 LLMController 응답 분리

* Feat: 폴더 관련 기능 구현 (#6)

* Init: 프로젝트 기본설정 세팅

- 프로젝트 생성
- .gitignore설정
- 프로젝트 의존성 추가
- application.yml 설정파일 구성

* Init: 프로젝트 기본 구조 및 공통 컴포넌트 설정

- 공통 설정 클래스 추가 (JPA, QueryDSL, Swagger, Web)
- 공통 도메인 엔티티 (RootEntity) 정의
- 예외 처리 관련 클래스 및 타입 구현
- JSON 변환을 위한 AttributeConverter 추가
- 유틸리티 클래스 (Math) 추가

* Chore: Folder 도메인 폴더 구조 셋업

폴더 구조 셋업 작업

* Feat: Folder 도메인의 엔티티 생성

엔티티 생성자, 부모-자식간 연결로직 생성

* refactor: 자기 참조 관계 설정 수정

기존, 다대일 양방향 관계에서 다대일 단방향 관계로 설정하고, 삭제 등의 이슈 발생시 Service 계층에서 함수의 재귀사용을 통해 삭제할 예정

* Chore: Document 도메인 폴더 구조 셋업

폴더 구조 셋업 및 엔티티 생성

* Feat: Lombok 라이브러리 활용하여 기본 생성자 생성

기본 생성자 생성 lombok 라이브러리 활용하여 대체

* chore: name 필드의 length 50으로 설정

name 필드 (Document, Folder) 의 length = 50 으로 설정

* chore: Domain 계층의 Repository가 QueryRepository 상속받도록 함

상속 작업 수행

* chore: Member 도메인 매핑 작업 수행

Member 도메인 매핑 작업 수행

* chore: Member 도메인과 Folder 도메인 연결 작업 수행

Member 도메인과 Folder 도메인 연결 작업 수행

* feat: 루트 폴더 생성하는 기능 구현

루트 폴더 생성하는 기능 구현

* feat: 서브폴더 생성하는 기능 구현

서브 폴더 생성하는 기능 구현

* feat: 폴더를 루트로 이동시키는 기능 구현

폴더를 루트로 이동시키는 기능 구현

* feat: 새로운 폴더 내부로 이동시키는 기능 구현

새로운 폴더 내부로 이동시키는 기능 구현

* feat: 계층형 구조의 폴더 탐색 기능 구현

계층형 구조의 폴더 탐색 기능 구현

* test: 재귀적으로 폴더를 조회하는 테스트 코드 작성

재귀적으로 폴더 조회하는 테스트코드 작성

* remove: 사용하지 않는 QueryDSL 관련 파일 삭제

사용하지 않는 QueryDSL 관련 파일 삭제

* refactor: formatting 적용

formatting 적용

* feat: 폴더 재귀적으로 삭제하는 기능 구현

폴더 재귀적으로 삭제하는 기능 구현

* feat: @onDelete 어노테이션을 사용하여 삭제 기능 구현

삭제 기능 구현

* feat: 폴더 구조의 조회를 간편하게 개선

폴더 구조의 조회 간편하게 개선

* feat: 루트에 폴더를 생성하는 API 구현

루트에 폴더를 생성하는 API 구현

* feat: 서브 폴더를 생성하는 API 구현

서브 폴더 생성하는 API 구현

* feat: 폴더 이동하는 API 구현

폴더 이동하는 API 구현

* refactor: 중복된 함수 기능 병합 작업 수행

중복된 함수 기능 병합 작업 수행

* feat: 폴더 조회 API 구현

폴더 조회 API 구현

* feat: 폴더 삭제 API 구현

폴더 삭제 API 구현

* rename: 함수명 변경

함수 명 변경

* refactor: 메서드 분리 작업 수행

메서드 분리 작업 수행

* refactor: Delete API 204 로 반환

204로 반환

* feat: 요청마다 DTO를 다르게 설정

요청마다 DTO 다르게 설정

* refactor: 타입추론방식에서 타입명시방식으로 변경

타입명시방식으로 코드 스타일 변경

* refactor: 도메인 값에 대한 검증은 도메인계층으로 옮김

도메인 계층으로 값에 대한 검증 이동

* refactor: Owner가 아닌 폴더에 접근하려고 하는 경우 NotFoundException 예외 발생

예외 발생

---------

Co-authored-by: rladbrua0207 <[email protected]>

* Style: 코드 포맷팅 통일

* Refactor: 예외 종류 변경

---------

Co-authored-by: 윤정훈 <[email protected]>
Co-authored-by: rladbrua0207 <[email protected]>

* Revert "Style: 코드 포맷팅 통일 (#36)" (#38)

This reverts commit ad9062e.

* Feat: 폴더 관련 기능 구현 (#39)

* Feat: 요약 및 문제 생성 API 구현 (#4)

* Rename: AI Task 도메인 이름 변경

- llm 으로 변경

* Refactor: LLM 도메인 애플리케이션 계층과 프레젠테이션 계층 응답 분리

* Feat: LLM 작업 진행 상태 확인 기능 구현

* Feat: 요약 및 문제 결과 조회 기능 구현

* Feat: 요약 및 문제 생성 기능 구현

- AI 서버와 통신하는 부분 제외하고 기능 구현
- 임시 UUID 를 통해 task 저장

* Feat: LLM 서버 콜백 기능 구현

- LLM 서버가 API 콜을 통해 페이지별 요약 및 문제 내용 전달
- task id 를 통해 조회하여 요약 내용과 문제 내용 업데이트

* Refactor: 변수 이름 변경

- 필드명 카멜케이스로 변경

* Refactor: 통일성 없는 부분 수정

- 필드명 변경
- 변수 추출

* Refactor: 예외 종류, 메서드 네이밍 변경

- LLMQueryService 예외 타입 변경
- SummaryAndProblemUpdateResponse 메서드 네이밍 변경

* Refactor: LLMQueryService 응답과 LLMController 응답 분리

* Feat: 폴더 관련 기능 구현 (#6)

* Init: 프로젝트 기본설정 세팅

- 프로젝트 생성
- .gitignore설정
- 프로젝트 의존성 추가
- application.yml 설정파일 구성

* Init: 프로젝트 기본 구조 및 공통 컴포넌트 설정

- 공통 설정 클래스 추가 (JPA, QueryDSL, Swagger, Web)
- 공통 도메인 엔티티 (RootEntity) 정의
- 예외 처리 관련 클래스 및 타입 구현
- JSON 변환을 위한 AttributeConverter 추가
- 유틸리티 클래스 (Math) 추가

* Chore: Folder 도메인 폴더 구조 셋업

폴더 구조 셋업 작업

* Feat: Folder 도메인의 엔티티 생성

엔티티 생성자, 부모-자식간 연결로직 생성

* refactor: 자기 참조 관계 설정 수정

기존, 다대일 양방향 관계에서 다대일 단방향 관계로 설정하고, 삭제 등의 이슈 발생시 Service 계층에서 함수의 재귀사용을 통해 삭제할 예정

* Chore: Document 도메인 폴더 구조 셋업

폴더 구조 셋업 및 엔티티 생성

* Feat: Lombok 라이브러리 활용하여 기본 생성자 생성

기본 생성자 생성 lombok 라이브러리 활용하여 대체

* chore: name 필드의 length 50으로 설정

name 필드 (Document, Folder) 의 length = 50 으로 설정

* chore: Domain 계층의 Repository가 QueryRepository 상속받도록 함

상속 작업 수행

* chore: Member 도메인 매핑 작업 수행

Member 도메인 매핑 작업 수행

* chore: Member 도메인과 Folder 도메인 연결 작업 수행

Member 도메인과 Folder 도메인 연결 작업 수행

* feat: 루트 폴더 생성하는 기능 구현

루트 폴더 생성하는 기능 구현

* feat: 서브폴더 생성하는 기능 구현

서브 폴더 생성하는 기능 구현

* feat: 폴더를 루트로 이동시키는 기능 구현

폴더를 루트로 이동시키는 기능 구현

* feat: 새로운 폴더 내부로 이동시키는 기능 구현

새로운 폴더 내부로 이동시키는 기능 구현

* feat: 계층형 구조의 폴더 탐색 기능 구현

계층형 구조의 폴더 탐색 기능 구현

* test: 재귀적으로 폴더를 조회하는 테스트 코드 작성

재귀적으로 폴더 조회하는 테스트코드 작성

* remove: 사용하지 않는 QueryDSL 관련 파일 삭제

사용하지 않는 QueryDSL 관련 파일 삭제

* refactor: formatting 적용

formatting 적용

* feat: 폴더 재귀적으로 삭제하는 기능 구현

폴더 재귀적으로 삭제하는 기능 구현

* feat: @onDelete 어노테이션을 사용하여 삭제 기능 구현

삭제 기능 구현

* feat: 폴더 구조의 조회를 간편하게 개선

폴더 구조의 조회 간편하게 개선

* feat: 루트에 폴더를 생성하는 API 구현

루트에 폴더를 생성하는 API 구현

* feat: 서브 폴더를 생성하는 API 구현

서브 폴더 생성하는 API 구현

* feat: 폴더 이동하는 API 구현

폴더 이동하는 API 구현

* refactor: 중복된 함수 기능 병합 작업 수행

중복된 함수 기능 병합 작업 수행

* feat: 폴더 조회 API 구현

폴더 조회 API 구현

* feat: 폴더 삭제 API 구현

폴더 삭제 API 구현

* rename: 함수명 변경

함수 명 변경

* refactor: 메서드 분리 작업 수행

메서드 분리 작업 수행

* refactor: Delete API 204 로 반환

204로 반환

* feat: 요청마다 DTO를 다르게 설정

요청마다 DTO 다르게 설정

* refactor: 타입추론방식에서 타입명시방식으로 변경

타입명시방식으로 코드 스타일 변경

* refactor: 도메인 값에 대한 검증은 도메인계층으로 옮김

도메인 계층으로 값에 대한 검증 이동

* refactor: Owner가 아닌 폴더에 접근하려고 하는 경우 NotFoundException 예외 발생

예외 발생

---------

Co-authored-by: rladbrua0207 <[email protected]>

---------

Co-authored-by: 윤정훈 <[email protected]>
Co-authored-by: rladbrua0207 <[email protected]>

* Style: 코드 포맷팅 통일 (#40)

* Style: 코드 포맷팅 통일

* Refactor: 예외 종류 변경

---------

Co-authored-by: 윤정훈 <[email protected]>
Co-authored-by: rladbrua0207 <[email protected]>

* Remove: .idea 폴더 삭제

* Style: 코드 포맷팅 통일 (#41)

* Feat: 녹음 파일 업로드 기능 구현 (#8)

* Feat: 녹음 파일 업로드 기능 구현

- Recording 엔티티, 레포지토리, 컨트롤러 코드 작성
- 오디오 디코딩, 파일 저장 코드 작성

* Chore: Weekly5 로 rebase

* Refactor: file base path @value 를 사용하도록 변경

---------

Co-authored-by: 윤정훈 <[email protected]>
Co-authored-by: rladbrua0207 <[email protected]>

* Feat: 녹음-페이지 저장 기능 구현 (#10)

* Refactor: 메서드, 파라미터 이름 변경

* Feat: 녹음-페이지 저장 기능 구현

- 페이지 넘김 이벤트에 따라 녹음-페이지 테이블에 타임스탬프 저장

* Refactor: 예외 메시지 수정

* Feature/annotation 구현 완료 (#12)

* Feat : Annotation CRUD 구현

1. Controller : API 명세서 구현
2. Service : R-CUD를 QueryService, Serivce를 이용하여 구현
3. presentation : DTO 구현

* Refactor: @positive를 이용한 양수 검증

* Refactor: @NoArgsConstructor의 접근 수준을 PROTECTED로 변경

* Refactor: CreateAnnotationRequest에서 좌표 및 크기 검증 추가

* Refactor: DTO를 record로 통일

* Refactor: getById로 변경

* Refactor: record로 인한 형식 변경

* Refactor: 정적 팩토리 from으로 변경

* Refactor: ManyToOne의 fetch 형식 LAZY로 설정

* Refactor : 정적팩토리 from으로 인한 코드 변경

* Refactor : createAnnotation에서 누락된  savedAnnotatio 추가

* Refactor : pageNumbers 누락 -> 해당 내용을 반영한 Read 구현

* Refactor: CRUD test code 작성

* Refactor : getById로 변경

* Revert "Feature/annotation 구현 완료 (#12)" (#14)

This reverts commit 0dcf1d2.

* Feat: Annotation API 구현 (#15)

* Feat : Annotation CRUD 구현

1. Controller : API 명세서 구현
2. Service : R-CUD를 QueryService, Serivce를 이용하여 구현
3. presentation : DTO 구현

* Refactor: @positive를 이용한 양수 검증

* Refactor: @NoArgsConstructor의 접근 수준을 PROTECTED로 변경

* Refactor: CreateAnnotationRequest에서 좌표 및 크기 검증 추가

* Refactor: DTO를 record로 통일

* Refactor: getById로 변경

* Refactor: record로 인한 형식 변경

* Refactor: 정적 팩토리 from으로 변경

* Refactor: ManyToOne의 fetch 형식 LAZY로 설정

* Refactor : 정적팩토리 from으로 인한 코드 변경

* Refactor : createAnnotation에서 누락된  savedAnnotatio 추가

* Refactor : pageNumbers 누락 -> 해당 내용을 반영한 Read 구현

* Refactor: CRUD test code 작성

* Refactor : getById로 변경

---------

Co-authored-by: mingjuu <[email protected]>
Co-authored-by: Minju Song <[email protected]>

* Feat: 문서 도메인 관련 기능 구현 (#13)

* refactor: 폴더의 삭제 방법 재귀형태로 찾아 삭제하도록 개선

개선

* chore: API 매칭 URL 수정 작업 진행

API 매칭 URL 수정 작업 진행

* remove: 불필요한 문서 상태 삭제

불필요한 문서 상태 삭제

* feat: PDF 저장하는 기능 구현

* remove: 불필요한 라이브러리 삭제

불필요한 라이브러리 삭제

* feat: Document 저장하는 기능 구현

* test: PDF Service에서 PDF 저장 로직 테스트코드 작성

PDF 저장 로직 테스트코드 작성

* feat: Document 저장 하는 기능 구현

Document 저장하는 기능 구현

* chore: PDF 처리 및 OCR 라이브러리

라이브러리 import

* feat: Document 생성 API 구현

Document 생성 API 구현

* feat: 자료 이름 수정 기능 구현

자료 이름 수정 기능 구현

* feat: 자료 조회 기능 구현

자료 조회 기능 구현

* feat: 자료 삭제 기능 구현

자료 삭제 기능 구현

* feat: 폴더 삭제시 자료도 함께 삭제되도록 기능 구현

삭제 기능 구현

* chore: PDF Setup

pdf 세팅

* feat: 루트(메인)에 생성되는 Document 설정

루트 자료 추가 기능 구현

---------

Co-authored-by: rladbrua0207 <[email protected]>
Co-authored-by: mingjuu <[email protected]>
Co-authored-by: Hyun-Seo Jeong <[email protected]>

* Fix: 에러 충돌 해결 및 기능 추가 (#16)

* Refactor: 코드 취합 후 수정

- document findById 를 getById 로 변경 등

---------

Co-authored-by: yugyeom <[email protected]>
Co-authored-by: 윤정훈 <[email protected]>
Co-authored-by: Minju Song <[email protected]>
Co-authored-by: mingjuu <[email protected]>
  • Loading branch information
5 people authored Oct 4, 2024
1 parent d23b60b commit 3f4ca1b
Show file tree
Hide file tree
Showing 91 changed files with 1,945 additions and 167 deletions.
6 changes: 6 additions & 0 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,12 @@ dependencies {
// Test
testImplementation 'org.springframework.boot:spring-boot-starter-test'
testRuntimeOnly 'org.junit.platform:junit-platform-launcher'

// PDF
implementation 'org.apache.pdfbox:pdfbox:3.0.2'

// OCR
implementation 'net.sourceforge.tess4j:tess4j:5.13.0'
}

tasks.named('test') {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
package notai.annotation.application;

import lombok.RequiredArgsConstructor;
import notai.annotation.domain.Annotation;
import notai.annotation.domain.AnnotationRepository;
import notai.annotation.presentation.response.AnnotationResponse;
import notai.common.exception.type.NotFoundException;
import notai.document.domain.DocumentRepository;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.List;
import java.util.stream.Collectors;

@Service
@RequiredArgsConstructor
public class AnnotationQueryService {

private final AnnotationRepository annotationRepository;
private final DocumentRepository documentRepository;

@Transactional(readOnly = true)
public List<AnnotationResponse> getAnnotationsByDocumentAndPageNumbers(Long documentId, List<Integer> pageNumbers) {
documentRepository.getById(documentId);

List<Annotation> annotations = annotationRepository.findByDocumentIdAndPageNumberIn(documentId, pageNumbers);
if (annotations.isEmpty()) {
throw new NotFoundException("해당 문서에 해당 페이지 번호의 주석이 존재하지 않습니다.");
}

return annotations.stream()
.map(AnnotationResponse::from)
.collect(Collectors.toList());
}
}
42 changes: 42 additions & 0 deletions src/main/java/notai/annotation/application/AnnotationService.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
package notai.annotation.application;

import lombok.RequiredArgsConstructor;
import notai.annotation.domain.Annotation;
import notai.annotation.domain.AnnotationRepository;
import notai.annotation.presentation.response.AnnotationResponse;
import notai.document.domain.Document;
import notai.document.domain.DocumentRepository;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

@Service
@RequiredArgsConstructor
public class AnnotationService {

private final AnnotationRepository annotationRepository;
private final DocumentRepository documentRepository;

@Transactional
public AnnotationResponse createAnnotation(Long documentId, int pageNumber, int x, int y, int width, int height, String content) {
Document document = documentRepository.getById(documentId);

Annotation annotation = new Annotation(document, pageNumber, x, y, width, height, content);
Annotation savedAnnotation = annotationRepository.save(annotation);
return AnnotationResponse.from(savedAnnotation);
}

@Transactional
public AnnotationResponse updateAnnotation(Long documentId, Long annotationId, int x, int y, int width, int height, String content) {
documentRepository.getById(documentId);
Annotation annotation = annotationRepository.getById(annotationId);
annotation.updateAnnotation(x, y, width, height, content);
return AnnotationResponse.from(annotation);
}

@Transactional
public void deleteAnnotation(Long documentId, Long annotationId) {
documentRepository.getById(documentId);
Annotation annotation = annotationRepository.getById(annotationId);
annotationRepository.delete(annotation);
}
}
66 changes: 66 additions & 0 deletions src/main/java/notai/annotation/domain/Annotation.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
package notai.annotation.domain;

import jakarta.persistence.*;
import jakarta.validation.constraints.NotNull;
import lombok.AccessLevel;
import lombok.Getter;
import lombok.NoArgsConstructor;
import notai.common.domain.RootEntity;
import notai.document.domain.Document;

@Getter
@Entity
@NoArgsConstructor(access = AccessLevel.PROTECTED)
@Table(name = "annotation")
public class Annotation extends RootEntity<Long> {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;

@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "document_id")
@NotNull
private Document document;

@NotNull
private int pageNumber;

@NotNull
private int x;

@NotNull
private int y;

@NotNull
private int width;

@NotNull
private int height;

@Column(columnDefinition = "TEXT")
@NotNull
private String content;

@Override
public Long getId() {
return this.id;
}

public Annotation(Document document, int pageNumber, int x, int y, int width, int height, String content) {
this.document = document;
this.pageNumber = pageNumber;
this.x = x;
this.y = y;
this.width = width;
this.height = height;
this.content = content;
}

public void updateAnnotation(int x, int y, int width, int height, String content) {
this.x = x;
this.y = y;
this.width = width;
this.height = height;
this.content = content;
}
}
20 changes: 20 additions & 0 deletions src/main/java/notai/annotation/domain/AnnotationRepository.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package notai.annotation.domain;

import notai.common.exception.type.NotFoundException;
import org.springframework.data.jpa.repository.JpaRepository;

import java.util.List;
import java.util.Optional;

public interface AnnotationRepository extends JpaRepository<Annotation, Long> {

List<Annotation> findByDocumentIdAndPageNumberIn(Long documentId, List<Integer> pageNumbers);


Optional<Annotation> findByIdAndDocumentId(Long id, Long documentId);

default Annotation getById(Long annotationId) {
return findById(annotationId)
.orElseThrow(() -> new NotFoundException("주석을 찾을 수 없습니다. ID: " + annotationId));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
package notai.annotation.presentation;

import jakarta.validation.Valid;
import lombok.RequiredArgsConstructor;
import notai.annotation.application.AnnotationQueryService;
import notai.annotation.application.AnnotationService;
import notai.annotation.presentation.request.CreateAnnotationRequest;
import notai.annotation.presentation.response.AnnotationResponse;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;

import java.util.List;

@RestController
@RequestMapping("/api/documents/{documentId}/annotations")
@RequiredArgsConstructor
public class AnnotationController {

private final AnnotationService annotationService;
private final AnnotationQueryService annotationQueryService;

@PostMapping
public ResponseEntity<AnnotationResponse> createAnnotation(
@PathVariable Long documentId, @RequestBody @Valid CreateAnnotationRequest request
) {

AnnotationResponse response = annotationService.createAnnotation(documentId,
request.pageNumber(),
request.x(),
request.y(),
request.width(),
request.height(),
request.content()
);

return new ResponseEntity<>(response, HttpStatus.CREATED);
}


@GetMapping
public ResponseEntity<List<AnnotationResponse>> getAnnotations(
@PathVariable Long documentId, @RequestParam List<Integer> pageNumbers
) {

List<AnnotationResponse> response = annotationQueryService.getAnnotationsByDocumentAndPageNumbers(
documentId,
pageNumbers
);

return new ResponseEntity<>(response, HttpStatus.OK);
}

@PutMapping("/{annotationId}")
public ResponseEntity<AnnotationResponse> updateAnnotation(
@PathVariable Long documentId,
@PathVariable Long annotationId,
@RequestBody @Valid CreateAnnotationRequest request
) {

AnnotationResponse response = annotationService.updateAnnotation(documentId,
annotationId,
request.x(),
request.y(),
request.width(),
request.height(),
request.content()
);

return new ResponseEntity<>(response, HttpStatus.OK);
}

@DeleteMapping("/{annotationId}")
public ResponseEntity<Void> deleteAnnotation(
@PathVariable Long documentId, @PathVariable Long annotationId
) {

annotationService.deleteAnnotation(documentId, annotationId);
return new ResponseEntity<>(HttpStatus.NO_CONTENT);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package notai.annotation.presentation.request;

import jakarta.validation.constraints.Max;
import jakarta.validation.constraints.Positive;
import jakarta.validation.constraints.PositiveOrZero;

public record CreateAnnotationRequest(

@Positive(message = "페이지 번호는 양수여야 합니다.")
int pageNumber,

// @Max(value = ?, message = "x 좌표는 최대 ? 이하여야 합니다.")
@PositiveOrZero(message = "x 좌표는 0 이상이어야 합니다.")
int x,

// @Max(value = ?, message = "x 좌표는 최대 ? 이하여야 합니다.")
@PositiveOrZero(message = "y 좌표는 0 이상이어야 합니다.")
int y,

// @Max(value = ?, message = "width는 최대 ? 이하여야 합니다.")
@Positive(message = "width는 양수여야 합니다.")
int width,

// @Max(value = ?, message = "height는 최대 ? 이하여야 합니다.")
@Positive(message = "height는 양수여야 합니다.")
int height,

String content
) {}
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
package notai.annotation.presentation.response;

import notai.annotation.domain.Annotation;

public record AnnotationResponse(
Long id,
Long documentId,
int pageNumber,
int x,
int y,
int width,
int height,
String content,
String createdAt,
String updatedAt
) {

public static AnnotationResponse from(Annotation annotation) {
return new AnnotationResponse(
annotation.getId(),
annotation.getDocument().getId(),
annotation.getPageNumber(),
annotation.getX(),
annotation.getY(),
annotation.getWidth(),
annotation.getHeight(),
annotation.getContent(),
annotation.getCreatedAt().toString(),
annotation.getUpdatedAt().toString()
);
}
}
5 changes: 4 additions & 1 deletion src/main/java/notai/auth/TokenPair.java
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
package notai.auth;

public record TokenPair(String accessToken, String refreshToken) {
public record TokenPair(
String accessToken,
String refreshToken
) {
}
28 changes: 11 additions & 17 deletions src/main/java/notai/auth/TokenService.java
Original file line number Diff line number Diff line change
Expand Up @@ -29,20 +29,17 @@ public TokenService(TokenProperty tokenProperty, MemberRepository memberReposito
}

public String createAccessToken(Long memberId) {
return Jwts.builder()
.claim(MEMBER_ID_CLAIM, memberId)
.issuedAt(new Date())
.expiration(new Date(System.currentTimeMillis() + accessTokenExpirationMillis))
.signWith(secretKey, Jwts.SIG.HS512)
.compact();
return Jwts.builder().claim(MEMBER_ID_CLAIM,
memberId
).issuedAt(new Date()).expiration(new Date(System.currentTimeMillis() + accessTokenExpirationMillis)).signWith(secretKey,
Jwts.SIG.HS512
).compact();
}

private String createRefreshToken() {
return Jwts.builder()
.issuedAt(new Date())
.expiration(new Date(System.currentTimeMillis() + refreshTokenExpirationMillis))
.signWith(secretKey, Jwts.SIG.HS512)
.compact();
return Jwts.builder().issuedAt(new Date()).expiration(new Date(System.currentTimeMillis() + refreshTokenExpirationMillis)).signWith(secretKey,
Jwts.SIG.HS512
).compact();
}

public TokenPair createTokenPair(Long memberId) {
Expand Down Expand Up @@ -71,12 +68,9 @@ public TokenPair refreshTokenPair(String refreshToken) {

public Long extractMemberId(String token) {
try {
return Jwts.parser()
.verifyWith(secretKey)
.build()
.parseSignedClaims(token)
.getPayload()
.get(MEMBER_ID_CLAIM, Long.class);
return Jwts.parser().verifyWith(secretKey).build().parseSignedClaims(token).getPayload().get(MEMBER_ID_CLAIM,
Long.class
);
} catch (Exception e) {
throw new UnAuthorizedException("유효하지 않은 토큰입니다.");
}
Expand Down
Loading

0 comments on commit 3f4ca1b

Please sign in to comment.