From 136fde3162c11409dc469cbe87fdc03ffe41b1ce Mon Sep 17 00:00:00 2001 From: junhaa <2171326@hansung.ac.kr> Date: Wed, 8 May 2024 18:47:16 +0900 Subject: [PATCH] =?UTF-8?q?:sparkles:=20FEAT.=20=EB=8F=99=ED=99=94=20?= =?UTF-8?q?=EC=9D=8C=EC=84=B1,=20=EC=82=AC=EC=A7=84=20=EB=8D=B0=EC=9D=B4?= =?UTF-8?q?=ED=84=B0=20=EC=A1=B0=ED=9A=8C=20API=20=EA=B8=B0=EB=8A=A5=20?= =?UTF-8?q?=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../repository/CustomCharacterRepository.java | 8 ++- .../OriginalCharacterRepository.java | 8 ++- .../service/FaceSwapQueryService.java | 9 ++++ .../service/FaceSwapQueryServiceImpl.java | 48 +++++++++++++++++ .../controller/FairytaleRestController.java | 53 +++++++++++++++++++ .../web/dto/FairytaleResponseDTO.java | 11 ++++ .../service/VoiceCommandServiceImpl.java | 17 +++--- .../voice/service/VoiceQueryService.java | 2 +- .../voice/service/VoiceQueryServiceImpl.java | 6 +-- .../web/controller/VoiceRestController.java | 4 +- .../global/enums/statuscode/ErrorStatus.java | 1 + 11 files changed, 150 insertions(+), 17 deletions(-) create mode 100644 src/main/java/fairytale/tbd/domain/faceSwap/service/FaceSwapQueryService.java create mode 100644 src/main/java/fairytale/tbd/domain/faceSwap/service/FaceSwapQueryServiceImpl.java diff --git a/src/main/java/fairytale/tbd/domain/faceSwap/repository/CustomCharacterRepository.java b/src/main/java/fairytale/tbd/domain/faceSwap/repository/CustomCharacterRepository.java index d0c2368..8b5a0e4 100644 --- a/src/main/java/fairytale/tbd/domain/faceSwap/repository/CustomCharacterRepository.java +++ b/src/main/java/fairytale/tbd/domain/faceSwap/repository/CustomCharacterRepository.java @@ -1,7 +1,13 @@ package fairytale.tbd.domain.faceSwap.repository; -import fairytale.tbd.domain.faceSwap.entity.CustomCharacter; +import java.util.List; + import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; + +import fairytale.tbd.domain.faceSwap.entity.CustomCharacter; +@Repository public interface CustomCharacterRepository extends JpaRepository { + List findByUserIdAndFairytaleId(Long userId, Long fairytaleId); } diff --git a/src/main/java/fairytale/tbd/domain/faceSwap/repository/OriginalCharacterRepository.java b/src/main/java/fairytale/tbd/domain/faceSwap/repository/OriginalCharacterRepository.java index 8704481..27fa53a 100644 --- a/src/main/java/fairytale/tbd/domain/faceSwap/repository/OriginalCharacterRepository.java +++ b/src/main/java/fairytale/tbd/domain/faceSwap/repository/OriginalCharacterRepository.java @@ -1,7 +1,13 @@ package fairytale.tbd.domain.faceSwap.repository; -import fairytale.tbd.domain.faceSwap.entity.OriginalCharacter; +import java.util.List; + import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; + +import fairytale.tbd.domain.faceSwap.entity.OriginalCharacter; +@Repository public interface OriginalCharacterRepository extends JpaRepository { + List findByFairytaleId(Long fairytaleId); } diff --git a/src/main/java/fairytale/tbd/domain/faceSwap/service/FaceSwapQueryService.java b/src/main/java/fairytale/tbd/domain/faceSwap/service/FaceSwapQueryService.java new file mode 100644 index 0000000..b4c681d --- /dev/null +++ b/src/main/java/fairytale/tbd/domain/faceSwap/service/FaceSwapQueryService.java @@ -0,0 +1,9 @@ +package fairytale.tbd.domain.faceSwap.service; + +import java.util.Map; + +import fairytale.tbd.domain.user.entity.User; + +public interface FaceSwapQueryService { + Map getFaceImages(User user, Long fairytaleId, boolean changeFace); +} diff --git a/src/main/java/fairytale/tbd/domain/faceSwap/service/FaceSwapQueryServiceImpl.java b/src/main/java/fairytale/tbd/domain/faceSwap/service/FaceSwapQueryServiceImpl.java new file mode 100644 index 0000000..d70c0de --- /dev/null +++ b/src/main/java/fairytale/tbd/domain/faceSwap/service/FaceSwapQueryServiceImpl.java @@ -0,0 +1,48 @@ +package fairytale.tbd.domain.faceSwap.service; + +import java.util.HashMap; +import java.util.Map; + +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import fairytale.tbd.domain.faceSwap.entity.CustomCharacter; +import fairytale.tbd.domain.faceSwap.entity.OriginalCharacter; +import fairytale.tbd.domain.faceSwap.repository.CustomCharacterRepository; +import fairytale.tbd.domain.faceSwap.repository.OriginalCharacterRepository; +import fairytale.tbd.domain.fairytale.entity.Fairytale; +import fairytale.tbd.domain.fairytale.exception.FairytaleNotFoundException; +import fairytale.tbd.domain.fairytale.repository.FairytaleRepository; +import fairytale.tbd.domain.user.entity.User; +import fairytale.tbd.global.enums.statuscode.ErrorStatus; +import lombok.RequiredArgsConstructor; + +@Service +@RequiredArgsConstructor +@Transactional(readOnly = true) +public class FaceSwapQueryServiceImpl implements FaceSwapQueryService { + private final FairytaleRepository fairytaleRepository; + private final OriginalCharacterRepository originalCharacterRepository; + private final CustomCharacterRepository customCharacterRepository; + + @Override + public Map getFaceImages(User user, Long fairytaleId, boolean changeFace) { + Map result = new HashMap<>(); + Fairytale fairytale = fairytaleRepository.findById(fairytaleId) + .orElseThrow(() -> new FairytaleNotFoundException( + ErrorStatus._FAIRYTALE_NOT_FOUND)); + + if (changeFace) { + for (CustomCharacter customCharacter : customCharacterRepository.findByUserIdAndFairytaleId(user.getId(), + fairytale.getId())) { + result.put(customCharacter.getPageNum(), customCharacter.getCustomURL()); + } + } else { + for (OriginalCharacter originalCharacter : originalCharacterRepository.findByFairytaleId( + fairytale.getId())) { + result.put(originalCharacter.getPageNum(), originalCharacter.getOriginalURL()); + } + } + return result; + } +} diff --git a/src/main/java/fairytale/tbd/domain/fairytale/web/controller/FairytaleRestController.java b/src/main/java/fairytale/tbd/domain/fairytale/web/controller/FairytaleRestController.java index f9285b6..186ad53 100644 --- a/src/main/java/fairytale/tbd/domain/fairytale/web/controller/FairytaleRestController.java +++ b/src/main/java/fairytale/tbd/domain/fairytale/web/controller/FairytaleRestController.java @@ -1,15 +1,31 @@ package fairytale.tbd.domain.fairytale.web.controller; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; +import fairytale.tbd.domain.faceSwap.service.FaceSwapQueryService; import fairytale.tbd.domain.fairytale.converter.Fairytaleconverter; import fairytale.tbd.domain.fairytale.entity.Fairytale; import fairytale.tbd.domain.fairytale.service.FairytaleCommandService; import fairytale.tbd.domain.fairytale.web.dto.FairytaleRequestDTO; import fairytale.tbd.domain.fairytale.web.dto.FairytaleResponseDTO; +import fairytale.tbd.domain.user.entity.User; +import fairytale.tbd.domain.voice.service.VoiceQueryService; +import fairytale.tbd.domain.voice.web.dto.VoiceResponseDTO; +import fairytale.tbd.global.annotation.LoginUser; +import fairytale.tbd.global.enums.statuscode.ErrorStatus; +import fairytale.tbd.global.exception.GeneralException; import fairytale.tbd.global.response.ApiResponse; import jakarta.validation.Valid; import lombok.RequiredArgsConstructor; @@ -18,7 +34,11 @@ @RequiredArgsConstructor @RequestMapping("/api/fairytale") public class FairytaleRestController { + private static final Logger LOGGER = LogManager.getLogger(FairytaleRestController.class); + private final FairytaleCommandService fairytaleCommandService; + private final VoiceQueryService voiceQueryService; + private final FaceSwapQueryService faceSwapQueryService; /** * 동화 추가 메서드 @@ -30,4 +50,37 @@ public ApiResponse addFairytale(@Val return ApiResponse.onSuccess(Fairytaleconverter.toAddFairytaleResultDTO(fairytale)); } + /** + * 동화 데이터 조회 메서드 + */ + @GetMapping("/{fairytaleId}") + public ApiResponse> getFairytaleWithId( + @LoginUser User user, + @PathVariable(name = "fairytaleId") Long fairytaleId, + @RequestParam(name = "change_voice") boolean changeVoice, + @RequestParam(name = "change_face") boolean changeFace) { + + Map> userTTSSegmentList = voiceQueryService.getUserTTSSegmentList( + user, fairytaleId, changeVoice); + + Map faceImages = faceSwapQueryService.getFaceImages(user, fairytaleId, changeFace); + + if (!userTTSSegmentList.keySet().equals(faceImages.keySet())) { + LOGGER.error("동화 데이터 호출 중 에러가 발생했습니다."); + throw new GeneralException(ErrorStatus._FAIRYTALE_DATA_NOT_EXIST); + } + + Map result = new HashMap<>(); + for (Long pageNum : userTTSSegmentList.keySet()) { + + FairytaleResponseDTO.GetFairytaleDetailDTO detailDTO = FairytaleResponseDTO.GetFairytaleDetailDTO.builder() + .voice_list(userTTSSegmentList.get(pageNum)) + .image_url(faceImages.get(pageNum)) + .build(); + + result.put(pageNum, detailDTO); + } + + return ApiResponse.onSuccess(result); + } } diff --git a/src/main/java/fairytale/tbd/domain/fairytale/web/dto/FairytaleResponseDTO.java b/src/main/java/fairytale/tbd/domain/fairytale/web/dto/FairytaleResponseDTO.java index 0f20fad..8cfdc56 100644 --- a/src/main/java/fairytale/tbd/domain/fairytale/web/dto/FairytaleResponseDTO.java +++ b/src/main/java/fairytale/tbd/domain/fairytale/web/dto/FairytaleResponseDTO.java @@ -1,7 +1,9 @@ package fairytale.tbd.domain.fairytale.web.dto; import java.time.LocalDateTime; +import java.util.List; +import fairytale.tbd.domain.voice.web.dto.VoiceResponseDTO; import lombok.AllArgsConstructor; import lombok.Builder; import lombok.Getter; @@ -16,4 +18,13 @@ public static class AddFairytaleResultDTO { private Long id; private LocalDateTime createdAt; } + + @Builder + @Getter + @NoArgsConstructor + @AllArgsConstructor + public static class GetFairytaleDetailDTO { + private List voice_list; + private String image_url; + } } diff --git a/src/main/java/fairytale/tbd/domain/voice/service/VoiceCommandServiceImpl.java b/src/main/java/fairytale/tbd/domain/voice/service/VoiceCommandServiceImpl.java index 7c9708e..5e0f838 100644 --- a/src/main/java/fairytale/tbd/domain/voice/service/VoiceCommandServiceImpl.java +++ b/src/main/java/fairytale/tbd/domain/voice/service/VoiceCommandServiceImpl.java @@ -18,7 +18,6 @@ import fairytale.tbd.domain.voice.entity.TTSSegment; import fairytale.tbd.domain.voice.entity.UserTTSSegment; import fairytale.tbd.domain.voice.entity.Voice; -import fairytale.tbd.domain.voice.entity.VoiceSample; import fairytale.tbd.domain.voice.exception.ExistVoiceException; import fairytale.tbd.domain.voice.exception.VoiceNotFoundException; import fairytale.tbd.domain.voice.exception.VoiceSaveErrorException; @@ -70,16 +69,16 @@ public Voice uploadVoice(VoiceRequestDTO.AddVoiceDTO request, User user) { throw new VoiceSaveErrorException(ErrorStatus._VOICE_SAVE_ERROR); } - String savePath = amazonS3Manager.uploadFile( - amazonS3Manager.generateS3SavePath(amazonS3Manager.VOICE_SAMPLE_PATH), - request.getSample()); - - VoiceSample voiceSample = VoiceSample.builder() - .url(savePath) - .build(); + // String savePath = amazonS3Manager.uploadFile( + // amazonS3Manager.generateS3SavePath(amazonS3Manager.VOICE_SAMPLE_PATH), + // request.getSample()); + // + // VoiceSample voiceSample = VoiceSample.builder() + // .url(savePath) + // .build(); Voice voice = VoiceConverter.toVoice(keyId); - voice.addVoiceSample(voiceSample); + // voice.addVoiceSample(voiceSample); user.setVoice(voice); voiceRepository.save(voice); diff --git a/src/main/java/fairytale/tbd/domain/voice/service/VoiceQueryService.java b/src/main/java/fairytale/tbd/domain/voice/service/VoiceQueryService.java index 5b2158d..2e0239c 100644 --- a/src/main/java/fairytale/tbd/domain/voice/service/VoiceQueryService.java +++ b/src/main/java/fairytale/tbd/domain/voice/service/VoiceQueryService.java @@ -8,5 +8,5 @@ public interface VoiceQueryService { Map> getUserTTSSegmentList(User user, - String fairytaleName); + Long fairytaleId, boolean changeVoice); } diff --git a/src/main/java/fairytale/tbd/domain/voice/service/VoiceQueryServiceImpl.java b/src/main/java/fairytale/tbd/domain/voice/service/VoiceQueryServiceImpl.java index c0dc793..9bbea93 100644 --- a/src/main/java/fairytale/tbd/domain/voice/service/VoiceQueryServiceImpl.java +++ b/src/main/java/fairytale/tbd/domain/voice/service/VoiceQueryServiceImpl.java @@ -46,10 +46,10 @@ public class VoiceQueryServiceImpl implements VoiceQueryService { @Transactional @Override public Map> getUserTTSSegmentList(User user, - String fairytaleName) { + Long fairytaleId, boolean changeVoice) { // 동화 이름이 유효한지 검증 // TODO Bean Validation으로 넘기기 - Fairytale fairytale = fairytaleRepository.findByName(fairytaleName) + Fairytale fairytale = fairytaleRepository.findById(fairytaleId) .orElseThrow(() -> new FairytaleNotFoundException(ErrorStatus._FAIRYTALE_NOT_FOUND)); // 동화의 문장 목록을 불러온 후, 페이지 번호, 문장 번호에 대해 오름차순 정렬 @@ -61,7 +61,7 @@ public Map> getUse for (Segment segment : segmentList) { VoiceResponseDTO.GetUserTTSSegmentResultDetailDTO resultDTO = null; // TODO 상속관계로 변경, ASYNC - if (segment.isMainCharacter()) { + if (segment.isMainCharacter() && changeVoice) { UserTTSSegment userTTSSegment = userTTSSegmentRepository.findByUserAndSegment(user, segment) .orElseGet(() -> voiceCommandService.saveUserTTSSegment(user, segment)); diff --git a/src/main/java/fairytale/tbd/domain/voice/web/controller/VoiceRestController.java b/src/main/java/fairytale/tbd/domain/voice/web/controller/VoiceRestController.java index 3c27d32..2e8f1cc 100644 --- a/src/main/java/fairytale/tbd/domain/voice/web/controller/VoiceRestController.java +++ b/src/main/java/fairytale/tbd/domain/voice/web/controller/VoiceRestController.java @@ -61,10 +61,10 @@ public ApiResponse addSegment( @GetMapping("/segment/test") public ApiResponse>> getUserSegment( @LoginUser User user, - @RequestParam(name = "fairytaleName") String fairytaleName) { + @RequestParam(name = "fairytaleId") Long fairytaleId) { LOGGER.info("getUserSegment START"); Map> userTTSSegmentList = voiceQueryService.getUserTTSSegmentList( - user, fairytaleName); + user, fairytaleId, true); return ApiResponse.onSuccess(userTTSSegmentList); } diff --git a/src/main/java/fairytale/tbd/global/enums/statuscode/ErrorStatus.java b/src/main/java/fairytale/tbd/global/enums/statuscode/ErrorStatus.java index dde9b47..6a1ba5b 100644 --- a/src/main/java/fairytale/tbd/global/enums/statuscode/ErrorStatus.java +++ b/src/main/java/fairytale/tbd/global/enums/statuscode/ErrorStatus.java @@ -17,6 +17,7 @@ public enum ErrorStatus implements BaseCode { _FAIRYTALE_NOT_FOUND(HttpStatus.BAD_REQUEST, "FAIRYTALE4001", "존재하지 않는 동화입니다."), _FAIRYTALE_EXIST_ERROR(HttpStatus.BAD_REQUEST, "FAIRYTALE4002", "이미 존재하는 이름의 동화입니다."), + _FAIRYTALE_DATA_NOT_EXIST(HttpStatus.INTERNAL_SERVER_ERROR, "FAIRYTALE5001", "동화 변환에 실패했습니다. 동화 데이터에 오류가 있습니다."), // ElevenLabs _FILE_CONVERT_ERROR(HttpStatus.INTERNAL_SERVER_ERROR, "FILE5001", "파일 변환에 실패했습니다."),