Skip to content

Commit cd6a3e2

Browse files
authored
Merge pull request #3 from AI-Tutor-2024/error
STT 추가 및 로직 리팩토링
2 parents 698ab59 + ba47ab7 commit cd6a3e2

File tree

14 files changed

+477
-157
lines changed

14 files changed

+477
-157
lines changed

build.gradle

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,8 @@ dependencies {
3434
implementation 'org.springframework.boot:spring-boot-starter-validation'
3535
implementation 'org.springframework.boot:spring-boot-starter-oauth2-client'
3636
implementation 'org.springframework.boot:spring-boot-starter-security'
37+
implementation 'com.knuddels:jtokkit:0.3.0'
38+
3739

3840
implementation group: 'org.springdoc', name: 'springdoc-openapi-starter-webmvc-ui', version: '2.1.0'
3941
testImplementation group: 'org.springdoc', name: 'springdoc-openapi-starter-webmvc-api', version: '2.1.0'

src/main/java/com/example/ai_tutor/domain/folder/application/FolderService.java

Lines changed: 26 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
package com.example.ai_tutor.domain.folder.application;
2-
32
import com.example.ai_tutor.domain.folder.domain.Folder;
43
import com.example.ai_tutor.domain.folder.domain.repository.FolderRepository;
54
import com.example.ai_tutor.domain.folder.dto.request.FolderCreateReq;
65
import com.example.ai_tutor.domain.folder.dto.response.FolderListRes;
76
import com.example.ai_tutor.domain.folder.dto.response.FolderNameListRes;
7+
import com.example.ai_tutor.domain.note.dto.response.FolderInfoRes;
88
import com.example.ai_tutor.domain.professor.domain.Professor;
99
import com.example.ai_tutor.domain.professor.domain.repository.ProfessorRepository;
1010
import com.example.ai_tutor.domain.user.domain.User;
@@ -33,7 +33,7 @@ public class FolderService {
3333
// 교수자 - 폴더 생성
3434
@Transactional
3535
public ResponseEntity<?> createNewFolder( UserPrincipal userPrincipal, FolderCreateReq folderCreateReq) {
36-
User user = validateUser(userPrincipal);
36+
User user = getUser(userPrincipal);
3737
String folderName = folderCreateReq.getFolderName();
3838
String professorName = folderCreateReq.getProfessorName();
3939

@@ -66,7 +66,7 @@ public ResponseEntity<?> createNewFolder( UserPrincipal userPrincipal, FolderCre
6666

6767
// 폴더 이름 목록 조회
6868
public ResponseEntity<?> getFolderNames(UserPrincipal userPrincipal) {
69-
User user = validateUser(userPrincipal);
69+
User user = getUser(userPrincipal);
7070
Professor professor = user.getProfessor();
7171
List<Folder> folders = professor.getFolders();
7272
List<FolderNameListRes> folderRes = folders.stream()
@@ -87,7 +87,7 @@ public ResponseEntity<?> getFolderNames(UserPrincipal userPrincipal) {
8787

8888
// 폴더 목록 조회 (폴더명, 교수자명 포함)
8989
public ResponseEntity<?> getAllFolders(UserPrincipal userPrincipal) {
90-
User user = validateUser(userPrincipal);
90+
User user = getUser(userPrincipal);
9191
Professor professor = user.getProfessor();
9292
List<Folder> folders = professor.getFolders();
9393
List<FolderListRes> folderRes = folders.stream()
@@ -108,9 +108,28 @@ public ResponseEntity<?> getAllFolders(UserPrincipal userPrincipal) {
108108
return ResponseEntity.ok(apiResponse);
109109
}
110110

111+
// 수업 정보 조회
112+
@Transactional
113+
public ResponseEntity<?> getFolderInfo(UserPrincipal userPrincipal, Long folderId) {
114+
getUser(userPrincipal);
115+
Folder folder = folderRepository.findById(folderId).orElseThrow(() -> new IllegalArgumentException("폴더를 찾을 수 없습니다."));
116+
117+
FolderInfoRes folderInfoRes = FolderInfoRes.builder()
118+
.folderName(folder.getFolderName())
119+
.professor(folder.getProfessor().getUser().getName())
120+
.build();
121+
122+
ApiResponse apiResponse = ApiResponse.builder()
123+
.check(true)
124+
.information(folderInfoRes)
125+
.build();
126+
127+
return ResponseEntity.ok(apiResponse);
128+
}
129+
111130
@Transactional
112131
public ResponseEntity<?> updateFolder(UserPrincipal userPrincipal, Long folderId, FolderCreateReq folderCreateReq) {
113-
validateUser(userPrincipal);
132+
getUser(userPrincipal);
114133
Folder folder=folderRepository.findById(folderId).orElseThrow(() -> new IllegalArgumentException("폴더를 찾을 수 없습니다."));
115134

116135

@@ -126,7 +145,7 @@ public ResponseEntity<?> updateFolder(UserPrincipal userPrincipal, Long folderId
126145

127146
@Transactional
128147
public ResponseEntity<?> deleteFolder(UserPrincipal userPrincipal, Long folderId) {
129-
validateUser(userPrincipal);
148+
getUser(userPrincipal);
130149
Folder folder=folderRepository.findById(folderId).orElseThrow(() -> new IllegalArgumentException("폴더를 찾을 수 없습니다."));
131150

132151
folderRepository.delete(folder);
@@ -138,7 +157,7 @@ public ResponseEntity<?> deleteFolder(UserPrincipal userPrincipal, Long folderId
138157
return ResponseEntity.ok(apiResponse);
139158
}
140159

141-
private User validateUser(UserPrincipal userPrincipal){
160+
private User getUser(UserPrincipal userPrincipal){
142161
return userRepository.findById(userPrincipal.getId()).orElseThrow(()
143162
-> new IllegalArgumentException("사용자를 찾을 수 없습니다."));
144163
}

src/main/java/com/example/ai_tutor/domain/folder/presentation/FolderController.java

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
import com.example.ai_tutor.domain.folder.dto.request.FolderCreateReq;
55
import com.example.ai_tutor.domain.folder.dto.response.FolderListRes;
66
import com.example.ai_tutor.domain.folder.dto.response.FolderNameListRes;
7+
import com.example.ai_tutor.domain.note.dto.response.FolderInfoRes;
78
import com.example.ai_tutor.global.config.security.token.CurrentUser;
89
import com.example.ai_tutor.global.config.security.token.UserPrincipal;
910
import com.example.ai_tutor.global.payload.ErrorResponse;
@@ -17,6 +18,7 @@
1718
import io.swagger.v3.oas.annotations.responses.ApiResponses;
1819
import lombok.RequiredArgsConstructor;
1920
import org.springframework.http.ResponseEntity;
21+
import org.springframework.security.core.annotation.AuthenticationPrincipal;
2022
import org.springframework.web.bind.annotation.*;
2123
import io.swagger.v3.oas.annotations.tags.Tag;
2224

@@ -54,6 +56,21 @@ public ResponseEntity<?> getAllFolders(
5456
return folderService.getAllFolders(userPrincipal);
5557
}
5658

59+
60+
@Operation(summary = "폴더 정보 조회 API", description = "노트를 생성하기 전 해당 노트의 폴더 - 강의명과 교수자명을 조회하는 API입니다.")
61+
@ApiResponses(value = {
62+
@ApiResponse(responseCode = "200", description = "조회 성공", content = { @Content(mediaType = "application/json", schema = @Schema(implementation = FolderInfoRes.class) ) } ),
63+
@ApiResponse(responseCode = "400", description = "조회 실패", content = { @Content(mediaType = "application/json", schema = @Schema(implementation = ErrorResponse.class) ) } ),
64+
})
65+
@GetMapping("/{folderId}/info")
66+
public ResponseEntity<?> getFolderInfoBeforeCreatingNote(
67+
@Parameter(description = "Access Token을 입력해주세요.", required = true) @AuthenticationPrincipal UserPrincipal userPrincipal,
68+
@Parameter(description = "folder의 id를 입력해주세요", required = true) @PathVariable Long folderId
69+
) {
70+
return folderService.getFolderInfo(userPrincipal, folderId);
71+
}
72+
73+
5774
@Operation(summary = "폴더 이름 목록 조회 API", description = "폴더 이름 목록을 조회하는 API입니다.")
5875
@ApiResponses(value = {
5976
@ApiResponse(responseCode = "200", description = "폴더 이름 목록 조회 성공", content = { @Content(mediaType = "application/json", schema = @Schema(implementation = FolderNameListRes.class) ) } ),

src/main/java/com/example/ai_tutor/domain/note/application/ProfessorNoteService.java

Lines changed: 47 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
package com.example.ai_tutor.domain.note.application;
22

33
import com.amazonaws.services.s3.AmazonS3;
4+
import com.example.ai_tutor.domain.answer.domain.repository.AnswerRepository;
45
import com.example.ai_tutor.domain.folder.domain.Folder;
56
import com.example.ai_tutor.domain.folder.domain.repository.FolderRepository;
6-
import com.example.ai_tutor.domain.answer.domain.repository.AnswerRepository;
77
import com.example.ai_tutor.domain.note.domain.Note;
88
import com.example.ai_tutor.domain.note.domain.NoteStatus;
99
import com.example.ai_tutor.domain.note.domain.repository.NoteRepository;
@@ -12,6 +12,7 @@
1212
import com.example.ai_tutor.domain.note_student.application.NoteStudentService;
1313
import com.example.ai_tutor.domain.note_student.domain.NoteStudent;
1414
import com.example.ai_tutor.domain.note_student.domain.repository.NoteStudentRepository;
15+
import com.example.ai_tutor.domain.openAPI.clova.ClovaService;
1516
import com.example.ai_tutor.domain.practice.domain.Practice;
1617
import com.example.ai_tutor.domain.practice.domain.repository.PracticeRepository;
1718
import com.example.ai_tutor.domain.professor.domain.Professor;
@@ -21,15 +22,19 @@
2122
import com.example.ai_tutor.global.DefaultAssert;
2223
import com.example.ai_tutor.global.config.security.token.UserPrincipal;
2324
import com.example.ai_tutor.global.payload.ApiResponse;
25+
import com.fasterxml.jackson.databind.JsonNode;
2426
import lombok.RequiredArgsConstructor;
27+
import lombok.extern.slf4j.Slf4j;
2528
import org.springframework.http.ResponseEntity;
2629
import org.springframework.stereotype.Service;
2730
import org.springframework.transaction.annotation.Transactional;
31+
import org.springframework.web.multipart.MultipartFile;
2832

2933
import java.util.Random;
3034
import java.util.List;
3135

3236
@Service
37+
@Slf4j
3338
@RequiredArgsConstructor
3439
@Transactional(readOnly = true)
3540
public class ProfessorNoteService {
@@ -42,41 +47,19 @@ public class ProfessorNoteService {
4247
private final NoteStudentRepository noteStudentRepository;
4348
private final ProfessorRepository professorRepository;
4449
private final NoteStudentService noteStudentService;
50+
private final ClovaService clovaService;
4551

4652
private final AmazonS3 amazonS3;
4753

48-
// 수업 정보 조회
49-
@Transactional
50-
public ResponseEntity<?> getFolderInfo(UserPrincipal userPrincipal, Long folderId) {
51-
validateUser(userPrincipal);
52-
Folder folder = folderRepository.findById(folderId).orElseThrow(() -> new IllegalArgumentException("폴더를 찾을 수 없습니다."));
53-
54-
FolderInfoRes folderInfoRes = FolderInfoRes.builder()
55-
.folderName(folder.getFolderName())
56-
.professor(folder.getProfessor().getUser().getName())
57-
.build();
58-
59-
ApiResponse apiResponse = ApiResponse.builder()
60-
.check(true)
61-
.information(folderInfoRes)
62-
.build();
63-
64-
return ResponseEntity.ok(apiResponse);
65-
}
66-
67-
6854
// 노트 생성
6955
@Transactional
70-
public ResponseEntity<?> createNewNote(UserPrincipal userPrincipal, Long folderId, NoteCreateReq noteCreateReq) {
71-
User user = validateUser(userPrincipal);
56+
public ResponseEntity<?> createNewNote(UserPrincipal userPrincipal, Long folderId, NoteCreateReq request) {
57+
User user = getUser(userPrincipal);
7258
Folder folder = folderRepository.findById(folderId).orElseThrow(() -> new IllegalArgumentException("폴더를 찾을 수 없습니다."));
7359
Professor professor = professorRepository.findByUser(user).orElseThrow(() -> new IllegalArgumentException("교수자를 찾을 수 없습니다."));
7460

7561
DefaultAssert.isTrue(folder.getProfessor().equals(professor), "해당 폴더에 접근할 수 없습니다.");
76-
Note note = Note.builder()
77-
.title(noteCreateReq.getTitle())
78-
.folder(folder)
79-
.build();
62+
Note note = Note.createNote(folder, request.getTitle(), null, null);
8063
noteRepository.save(note);
8164

8265
NoteAccessRes noteAccessRes = NoteAccessRes.builder()
@@ -91,6 +74,37 @@ public ResponseEntity<?> createNewNote(UserPrincipal userPrincipal, Long folderI
9174
return ResponseEntity.ok(apiResponse);
9275
}
9376

77+
@Transactional
78+
public boolean convertSpeechToText(Long noteId, MultipartFile file) {
79+
// 1. Note 조회
80+
Note note = noteRepository.findById(noteId)
81+
.orElseThrow(() -> new IllegalArgumentException("노트를 찾을 수 없습니다."));
82+
83+
// 2. 기존 STT 변환이 있다면 중단
84+
if (note.getSttText() != null) {
85+
log.info("기존 STT 변환 데이터가 존재하므로 변환하지 않습니다.");
86+
return false;
87+
}
88+
89+
try {
90+
// 3. CLOVA STT API 호출 (동기 처리)
91+
JsonNode response = clovaService.processSpeechToText(file).block();
92+
log.info("CLOVA STT 응답: {}", response);
93+
94+
// 4. STT 결과에서 텍스트 추출 및 저장
95+
String fullText = response.path("text").asText();
96+
note.updateStt(fullText, String.valueOf(response));
97+
noteRepository.save(note);
98+
99+
log.info("STT 변환 완료: {}", noteId);
100+
return true;
101+
} catch (Exception e) {
102+
log.error("STT 변환 중 오류 발생", e);
103+
note.markSttFailed(); // 실패 상태 업데이트
104+
noteRepository.save(note);
105+
return false;
106+
}
107+
}
94108

95109
//
96110
// // 녹음본이 아닌 영상을 업로드하는 방식으로 수정
@@ -153,7 +167,7 @@ public ResponseEntity<?> createNewNote(UserPrincipal userPrincipal, Long folderI
153167

154168
// 문제지 목록 조회
155169
public ResponseEntity<?> getAllNotesByFolder(UserPrincipal userPrincipal, Long folderId) {
156-
User user = validateUser(userPrincipal);
170+
User user = getUser(userPrincipal);
157171
Folder folder = folderRepository.findById(folderId).orElseThrow(() -> new IllegalArgumentException("폴더를 찾을 수 없습니다."));
158172
DefaultAssert.isTrue(user == folder.getProfessor().getUser(), "사용자가 일치하지 않습니다.");
159173

@@ -188,7 +202,7 @@ public ResponseEntity<?> getAllNotesByFolder(UserPrincipal userPrincipal, Long f
188202
// 문제지 삭제
189203
@Transactional
190204
public ResponseEntity<?> deleteNoteById(UserPrincipal userPrincipal, Long noteId) {
191-
User user = validateUser(userPrincipal);
205+
User user = getUser(userPrincipal);
192206
Note note = noteRepository.findById(noteId).orElseThrow(() -> new IllegalArgumentException("노트를 찾을 수 없습니다."));
193207

194208
Folder folder = note.getFolder();
@@ -221,7 +235,7 @@ public ResponseEntity<?> deleteNoteById(UserPrincipal userPrincipal, Long noteId
221235
// 문제지 랜덤 코드 생성
222236
@Transactional
223237
public ResponseEntity<?> createRandomCode(UserPrincipal userPrincipal, Long noteId) {
224-
validateUser(userPrincipal);
238+
getUser(userPrincipal);
225239
Note note = noteRepository.findById(noteId)
226240
.orElseThrow(() -> new IllegalArgumentException("노트를 찾을 수 없습니다."));
227241

@@ -265,7 +279,7 @@ private String generateRandomCode() {
265279
}
266280

267281
public ResponseEntity<?> getNoteResult(UserPrincipal userPrincipal, Long noteId) {
268-
validateUser(userPrincipal);
282+
getUser(userPrincipal);
269283
Note note = noteRepository.findById(noteId).orElseThrow(() -> new IllegalArgumentException("노트를 찾을 수 없습니다."));
270284

271285
List<NoteStudent> noteStudentList = noteStudentRepository.findByNote(note);
@@ -298,8 +312,8 @@ public ResponseEntity<?> getNoteResult(UserPrincipal userPrincipal, Long noteId)
298312

299313
}
300314

301-
private User validateUser(UserPrincipal userPrincipal){
302-
return userRepository.findById(userPrincipal.getId()).orElseThrow(()
315+
private User getUser(UserPrincipal userPrincipal){
316+
return userRepository.findById(1L).orElseThrow(()
303317
-> new IllegalArgumentException("사용자를 찾을 수 없습니다."));
304318
}
305319
}

0 commit comments

Comments
 (0)