11package com .driveu .server .domain .question .application ;
22
33import com .amazonaws .services .kms .model .NotFoundException ;
4- import com .amazonaws .services .s3 .AmazonS3Client ;
5- import com .amazonaws .services .s3 .model .S3Object ;
6- import com .amazonaws .services .s3 .model .S3ObjectInputStream ;
74import com .driveu .server .domain .directory .dao .DirectoryRepository ;
85import com .driveu .server .domain .directory .domain .Directory ;
96import com .driveu .server .domain .question .dao .QuestionRepository ;
129import com .driveu .server .domain .question .dto .request .QuestionCreateRequest ;
1310import com .driveu .server .domain .question .dto .response .QuestionListResponse ;
1411import com .driveu .server .domain .question .dto .response .QuestionResponse ;
15- import com .driveu .server .domain .resource .dao .FileRepository ;
16- import com .driveu .server .domain .resource .dao .NoteRepository ;
1712import com .driveu .server .domain .resource .dao .ResourceDirectoryRepository ;
1813import com .driveu .server .domain .resource .dao .ResourceRepository ;
19- import com .driveu .server .domain .resource .domain .File ;
20- import com .driveu .server .domain .resource .domain .Note ;
2114import com .driveu .server .domain .resource .domain .Resource ;
2215import com .driveu .server .domain .resource .domain .ResourceDirectory ;
23- import jakarta . persistence . EntityNotFoundException ;
16+ import com . driveu . server . infra . ai . AiService ;
2417import lombok .RequiredArgsConstructor ;
2518import org .jetbrains .annotations .NotNull ;
26- import org .springframework .beans .factory .annotation .Value ;
27- import org .springframework .core .io .ByteArrayResource ;
2819import org .springframework .http .*;
2920import org .springframework .stereotype .Service ;
3021import org .springframework .transaction .annotation .Transactional ;
31- import org .springframework .util .LinkedMultiValueMap ;
3222import org .springframework .util .MultiValueMap ;
33- import org .springframework .web .client .RestTemplate ;
3423
35- import java .io .IOException ;
36- import java .nio .charset .StandardCharsets ;
37- import java .nio .file .Paths ;
3824import java .util .HashSet ;
3925import java .util .List ;
4026import java .util .Set ;
@@ -49,13 +35,8 @@ public class QuestionService {
4935 private final QuestionResourceRepository questionResourceRepository ;
5036 private final ResourceRepository resourceRepository ;
5137 private final ResourceDirectoryRepository resourceDirectoryRepository ;
52- private final NoteRepository noteRepository ;
53- private final RestTemplate restTemplate ;
54- private final AmazonS3Client amazonS3Client ;
55- private final FileRepository fileRepository ;
56-
57- @ Value ("${spring.cloud.aws.s3.bucket}" )
58- private String bucketName ;
38+ private final AiService aiService ;
39+ private final QuestionResourceService questionResourceService ;
5940
6041 @ Transactional
6142 public QuestionResponse createQuestion (Long directoryId , List <QuestionCreateRequest > requestList ) {
@@ -81,31 +62,8 @@ public QuestionResponse createQuestion(Long directoryId, List<QuestionCreateRequ
8162 int version = getVersion (existingSameQuestions );
8263
8364 // resource type에 따라 파일을 추출해서 multipart/form-data 데이터 형식으로 만듦
84- MultiValueMap <String , Object > requestBody = createRequestBody (requestList );
85-
86- HttpHeaders headers = new HttpHeaders ();
87- headers .setContentType (MediaType .MULTIPART_FORM_DATA );
88-
89- HttpEntity <MultiValueMap <String , Object >> requestEntity =
90- new HttpEntity <>(requestBody , headers );
91-
92- // 3) AI 서버 URL (여러 파일을 받는 엔드포인트)
93- String aiUrl = "http://3.37.182.184:8000/api/ai/generate" ;
94-
95- // ResponseEntity<String> 으로 받아서 raw JSON 전체를 꺼냄
96- ResponseEntity <String > response = restTemplate .exchange (
97- aiUrl ,
98- HttpMethod .POST ,
99- requestEntity ,
100- String .class
101- );
102- if (response .getBody () == null ) {
103- throw new RuntimeException ("AI 서버 오류: " + response .getStatusCode ());
104- }
105-
106- String aiResponse = response .getBody ();
107-
108- System .out .println (aiResponse );
65+ MultiValueMap <String , Object > requestBody = questionResourceService .createRequestBody (requestList );
66+ String aiResponse = aiService .generateQuestion (requestBody );
10967
11068 Question question = Question .of (title , version , aiResponse );
11169 Question savedQuestion = questionRepository .save (question );
@@ -123,67 +81,6 @@ public QuestionResponse createQuestion(Long directoryId, List<QuestionCreateRequ
12381 return QuestionResponse .fromEntity (savedQuestion );
12482 }
12583
126- private @ NotNull MultiValueMap <String , Object > createRequestBody (List <QuestionCreateRequest > requestList ) {
127- MultiValueMap <String , Object > body = new LinkedMultiValueMap <>();
128-
129- for (QuestionCreateRequest request : requestList ) {
130- if (request .getType ().equals ("FILE" )){
131- // 파일에 저장된 s3Path로 S3에서 가져오기
132- addFileFromS3 (request , body );
133-
134- } else if (request .getType ().equals ("NOTE" )) {
135- // 노트 컨텐츠로 파일 만들기
136- addNote (request , body );
137- }
138- else {
139- throw new IllegalArgumentException ("잘못된 resource type 입니다." );
140- }
141- }
142- return body ;
143- }
144-
145- private void addNote (QuestionCreateRequest request , MultiValueMap <String , Object > body ) {
146- Note note = noteRepository .findById (request .getResourceId ())
147- .orElseThrow (() -> new EntityNotFoundException ("Note not found: " + request .getResourceId ()));
148-
149- String markdown = note .getContent ();
150-
151- // String → ByteArrayResource (가짜 파일)
152- ByteArrayResource fileResource = new ByteArrayResource (
153- markdown .getBytes (StandardCharsets .UTF_8 )
154- ) {
155- @ Override
156- public String getFilename () {
157- return "note-" + request .getResourceId () + ".md" ;
158- }
159- };
160-
161- // 같은 폼 필드명("files")에 여러 개를 add하면, 서버 쪽에서는 배열로 받는다.
162- body .add ("files" , fileResource );
163- }
164-
165- private void addFileFromS3 (QuestionCreateRequest request , MultiValueMap <String , Object > body ) {
166- File file = fileRepository .findById (request .getResourceId ())
167- .orElseThrow (() -> new EntityNotFoundException ("File not found: " + request .getResourceId ()));
168-
169- String s3Path = file .getS3Path ();
170- String filename = Paths .get (s3Path ).getFileName ().toString ();
171-
172- S3Object s3Object = amazonS3Client .getObject (bucketName , s3Path );
173- try (S3ObjectInputStream is = s3Object .getObjectContent ()) {
174- byte [] bytes = is .readAllBytes ();
175- ByteArrayResource fileResource = new ByteArrayResource (bytes ) {
176- @ Override
177- public String getFilename () {
178- return filename ;
179- }
180- };
181- body .add ("files" , fileResource );
182- } catch (IOException e ) {
183- throw new RuntimeException ("S3에서 파일 읽기 실패: " + s3Path , e );
184- }
185- }
186-
18784 private @ NotNull String createTitle (List <QuestionCreateRequest > requestList , Directory directory ) {
18885 String title ;
18986 if (requestList .getFirst ().getTagId () != null ) {
@@ -211,15 +108,15 @@ private static int getVersion(List<Question> existingSameQuestions) {
211108 return version ;
212109 }
213110
214- @ Transactional
111+ @ Transactional ( readOnly = true )
215112 public QuestionResponse getQuestionById (Long questionId ) {
216113 Question question = questionRepository .findById (questionId )
217114 .orElseThrow (() -> new NotFoundException ("Question not found" ));
218115
219116 return QuestionResponse .fromEntity (question );
220117 }
221118
222- @ Transactional
119+ @ Transactional ( readOnly = true )
223120 public List <QuestionListResponse > getQuestionsByUserSemester (Long userSemesterId ) {
224121
225122 // 1) 해당 학기에 속한 모든 디렉토리 조회
0 commit comments