55import everTale .everTale_be .domain .character .entity .enums .Gender ;
66import everTale .everTale_be .domain .character .repository .PersonalityRepository ;
77import everTale .everTale_be .domain .character .repository .StoryCharacterRepository ;
8+ import everTale .everTale_be .domain .easterEgg .entity .EasterEggVoice ;
89import everTale .everTale_be .domain .profile .entity .Enum .ProfileType ;
910import everTale .everTale_be .domain .profile .entity .Profile ;
1011import everTale .everTale_be .domain .profile .repository .ProfileRepository ;
3132
3233import java .util .HashSet ;
3334import java .util .List ;
35+ import java .util .Random ;
3436import java .util .Set ;
3537import java .util .stream .Collectors ;
3638
@@ -65,15 +67,24 @@ public StoryResponseDTO.StoryScenesResponseDTO getScenesOfStory(Long storyId) {
6567 }
6668
6769 @ Transactional
68- public void updateStoryTitle (Long storyId , String title ) {
70+ public void updateStoryTitleAndMainImage (Long storyId , String title ) {
6971 Story story = findMyStory (storyId );
70- story .updateTitle (title );
71- }
7272
73+ List <Scene > scenesWithImage = sceneRepository .findByStoryIdOrderByPageAsc (storyId )
74+ .stream ()
75+ .filter (s -> s .getImageUrl () != null && !s .getImageUrl ().isBlank ())
76+ .collect (Collectors .toList ());
77+
78+ if (!scenesWithImage .isEmpty ()) {
79+ Scene randomScene = scenesWithImage .get (new Random ().nextInt (scenesWithImage .size ()));
80+ story .updateImageUrl (randomScene .getImageUrl ());
81+ }
7382
83+ story .updateTitle (title );
84+ }
7485 @ Transactional
75- public String updateSceneContent (Long storyId , int sceneNum , String updatedContent ) {
76- Scene scene = findMyScene (storyId , sceneNum );
86+ public String updateSceneContent (Long storyId , int pageNum , String updatedContent ) {
87+ Scene scene = findMyScene (storyId , pageNum );
7788 scene .updateContent (updatedContent );
7889 return updatedContent ;
7990 }
@@ -84,7 +95,6 @@ public String updateSceneContent(Long storyId, int sceneNum, String updatedConte
8495 public void deleteStoryWithScenes (Long storyId ) {
8596 Story story = findMyStory (storyId );
8697 deleteS3AssetsOf (story );
87- // TODO: 여기에 EasterEggVoice S3 파일 삭제 로직 추가 필요
8898 storyRepository .delete (story );
8999 }
90100
@@ -103,18 +113,29 @@ private void deleteS3AssetsOf(Story story) {
103113 }
104114 }
105115
106- // 씬 이미지들 삭제
116+ // 씬 이미지 및 보이스파일 삭제
107117 List <Scene > scenes = sceneRepository .findByStoryIdOrderByPageAsc (story .getId ());
108118 Set <String > sceneImageUrls = new HashSet <>();
119+ Set <String > voiceFiles = new HashSet <>();
109120 for (Scene s : scenes ) {
110121 if (s .getImageUrl () != null && !s .getImageUrl ().isBlank ()) {
111122 sceneImageUrls .add (s .getImageUrl ());
112123 }
113-
124+ EasterEggVoice voice = s .getEasterEggVoice ();
125+ if (voice != null ) {
126+ String voiceFile = voice .getVoiceFile ();
127+ if (voiceFile != null && !voiceFile .isBlank ()) {
128+ voiceFiles .add (voiceFile );
129+ }
130+ }
114131 }
115132 for (String url : sceneImageUrls ) {
116133 s3Manager .deleteFileByS3Url (url );
117134 }
135+
136+ for ( String file : voiceFiles ){
137+ s3Manager .deleteFile (file );
138+ }
118139 }
119140
120141
@@ -182,7 +203,6 @@ public String generateInitScene(Long storyId, StoryRequestDTO.StoryWorldViewRequ
182203
183204 // 3. FastAPI 요청용 JSON 만들기
184205 StoryRequestDTO .FastApiInitStoryRequestDTO requestDto = StoryRequestDTO .FastApiInitStoryRequestDTO .builder ()
185- .title (story .getTitle ())
186206 .genre (request .getGenre ().name ())
187207 .worldView (request .getWorldView ())
188208 .name (storyCharacter .getName ())
@@ -214,9 +234,9 @@ public String generateInitScene(Long storyId, StoryRequestDTO.StoryWorldViewRequ
214234
215235 // 이전 장면 기반 다음 줄거리 생성
216236 @ Transactional
217- public String generateNextScene (Long storyId , int sceneNum ) {
237+ public String generateNextScene (Long storyId , int pageNum ) {
218238 // 1. 이전 줄거리 조회
219- Scene prevScene = findMyScene (storyId , sceneNum -1 );
239+ Scene prevScene = findMyScene (storyId , pageNum -1 );
220240 String previousContent = prevScene .getContent ();
221241
222242 // 2. Story & Character 조회
@@ -232,9 +252,8 @@ public String generateNextScene(Long storyId, int sceneNum) {
232252 StoryRequestDTO .NextStoryGenerateRequestDTO dto =
233253 StoryRequestDTO .NextStoryGenerateRequestDTO .builder ()
234254 .previous (previousContent )
235- .sceneNum ( sceneNum )
255+ .pageNum ( pageNum )
236256 .genre (story .getGenre ().name ())
237- .title (story .getTitle ())
238257 .name (character .getName ())
239258 .age (character .getAge ())
240259 .gender (character .getGender ().name ())
@@ -246,7 +265,7 @@ public String generateNextScene(Long storyId, int sceneNum) {
246265
247266 // 6. 새 Scene 저장
248267 Scene newScene = Scene .builder ()
249- .page (sceneNum )
268+ .page (pageNum )
250269 .content (nextContent )
251270 .build ();
252271 story .addScene (newScene );
@@ -256,31 +275,31 @@ public String generateNextScene(Long storyId, int sceneNum) {
256275
257276 // 이전 장면 기반 질문 생성
258277 @ Transactional (readOnly = true )
259- public String generateQuestionFromPreviousScene (Long storyId , int sceneNum ) {
260- Scene prevScene = findMyScene (storyId , sceneNum - 1 );
278+ public String generateQuestionFromPreviousScene (Long storyId , int pageNum ) {
279+ Scene prevScene = findMyScene (storyId , pageNum - 1 );
261280 return storyApiClient .callFastApiForQuestion (prevScene .getContent ());
262281 }
263282
264283 // 아이의 대답 기반 다음 줄거리 생성
265284 @ Transactional
266- public String generateNextSceneWithAnswer (Long storyId , int sceneNum , String answer ) {
267- Scene prevScene = findMyScene (storyId , sceneNum - 1 );
285+ public String generateNextSceneWithAnswer (Long storyId , int pageNum , String answer ) {
286+ Scene prevScene = findMyScene (storyId , pageNum - 1 );
268287
269288 String nextContent = storyApiClient .callFastApiForNextStoryWithAnswer (prevScene .getContent (), answer );
270289
271290 Story story = prevScene .getStory ();
272291
273292 Scene newScene = Scene .builder ()
274- .page (sceneNum )
293+ .page (pageNum )
275294 .content (nextContent )
276295 .build ();
277296 story .addScene (newScene );
278297 return nextContent ;
279298 }
280299 // 줄거리 및 아이그림 기반 그림 생성
281300 @ Transactional
282- public String generateImageFromSketch (Long storyId , int sceneNum , MultipartFile sketch ) {
283- Scene scene = findMyScene (storyId , sceneNum );
301+ public String generateImageFromSketch (Long storyId , int pageNum , MultipartFile sketch ) {
302+ Scene scene = findMyScene (storyId , pageNum );
284303
285304 String prompt = scene .getContent ();
286305 String imageUrl = storyApiClient .callFastApiForImageFromSketch (sketch , prompt , scene .getStory ().getGenre ().name ());
@@ -291,8 +310,8 @@ public String generateImageFromSketch(Long storyId, int sceneNum, MultipartFile
291310
292311 // 줄거리 기반 그림 생성
293312 @ Transactional
294- public String generateImageFromPrompt (Long storyId , int sceneNum ) {
295- Scene scene = findMyScene (storyId , sceneNum );
313+ public String generateImageFromPrompt (Long storyId , int pageNum ) {
314+ Scene scene = findMyScene (storyId , pageNum );
296315
297316 String prompt = scene .getContent ();
298317 String imageUrl = storyApiClient .callFastApiForImageFromPrompt (prompt , scene .getStory ().getGenre ().name ());
@@ -301,9 +320,9 @@ public String generateImageFromPrompt(Long storyId, int sceneNum) {
301320 return imageUrl ;
302321 }
303322
304- private Scene findMyScene (Long storyId , int sceneNum ){
323+ private Scene findMyScene (Long storyId , int pageNum ){
305324 Long profileId = profileHelper .getAuthenticatedProfileId ();
306- return sceneRepository .findByStoryIdAndPageAndStoryProfileId (storyId , sceneNum , profileId )
325+ return sceneRepository .findByStoryIdAndPageAndStoryProfileId (storyId , pageNum , profileId )
307326 .orElseThrow (() -> new NotFoundHandler (ErrorStatus .SCENE_NOT_FOUND ));
308327 }
309328 private Story findMyStory (Long storyId ) {
0 commit comments