diff --git a/backend/src/main/java/aimo/backend/domains/member/controller/MemberController.java b/backend/src/main/java/aimo/backend/domains/member/controller/MemberController.java index 61633a7..361be1c 100644 --- a/backend/src/main/java/aimo/backend/domains/member/controller/MemberController.java +++ b/backend/src/main/java/aimo/backend/domains/member/controller/MemberController.java @@ -13,30 +13,29 @@ import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; +import aimo.backend.common.dto.DataResponse; +import aimo.backend.common.security.filter.jwtFilter.JwtTokenProvider; import aimo.backend.common.util.memberLoader.MemberLoader; -import aimo.backend.domains.member.dto.parameter.DeleteProfileImageParameter; -import aimo.backend.domains.member.dto.parameter.SignUpParameter; -import aimo.backend.domains.member.dto.parameter.UpdateNicknameParameter; import aimo.backend.domains.member.dto.parameter.DeleteMemberParameter; +import aimo.backend.domains.member.dto.parameter.DeleteProfileImageParameter; import aimo.backend.domains.member.dto.parameter.FindMyInfoParameter; import aimo.backend.domains.member.dto.parameter.SaveFileMetaDataParameter; +import aimo.backend.domains.member.dto.parameter.SignUpParameter; +import aimo.backend.domains.member.dto.parameter.UpdateNicknameParameter; import aimo.backend.domains.member.dto.parameter.UpdatePasswordParameter; import aimo.backend.domains.member.dto.request.CheckNicknameExistsRequest; import aimo.backend.domains.member.dto.request.DeleteMemberRequest; import aimo.backend.domains.member.dto.request.LogoutRequest; -import aimo.backend.domains.member.dto.response.FindMyInfoResponse; -import aimo.backend.domains.member.dto.response.NicknameExistsResponse; import aimo.backend.domains.member.dto.request.SendTemporaryPasswordRequest; -import aimo.backend.common.dto.DataResponse; -import aimo.backend.common.security.filter.jwtFilter.JwtTokenProvider; import aimo.backend.domains.member.dto.request.UpdateNicknameRequest; import aimo.backend.domains.member.dto.request.UpdatePasswordRequest; +import aimo.backend.domains.member.dto.response.FindMyInfoResponse; +import aimo.backend.domains.member.dto.response.NicknameExistsResponse; +import aimo.backend.domains.member.service.MemberService; import aimo.backend.infrastructure.s3.S3Service; -import aimo.backend.infrastructure.s3.dto.request.CreatePresignedUrlRequest; -import aimo.backend.infrastructure.s3.dto.response.CreatePresignedUrlResponse; import aimo.backend.infrastructure.s3.dto.request.SaveFileMetaDataRequest; - -import aimo.backend.domains.member.service.MemberService; +import aimo.backend.infrastructure.s3.dto.response.CreatePreSignedUrlResponse; +import aimo.backend.infrastructure.s3.model.PresignedUrlPrefix; import jakarta.mail.MessagingException; import jakarta.servlet.http.HttpServletRequest; import jakarta.validation.Valid; @@ -82,13 +81,11 @@ public ResponseEntity> deleteMember(@RequestBody @Valid Delet } @GetMapping("/profile/presigned/{filename}") - public ResponseEntity> createProfileImagePreSignedUrl( + public ResponseEntity> createProfileImagePreSignedUrl( @PathVariable("filename") String filename ) { - CreatePresignedUrlRequest createPresignedUrlRequest = CreatePresignedUrlRequest.of(filename); - return ResponseEntity.status(HttpStatus.CREATED) - .body(DataResponse.created(s3Service.createProfilePresignedUrl(createPresignedUrlRequest))); + .body(DataResponse.created(s3Service.createPreSignedUrl(filename, PresignedUrlPrefix.IMAGE))); } @PostMapping("/profile/success") diff --git a/backend/src/main/java/aimo/backend/domains/privatePost/controller/PrivatePostController.java b/backend/src/main/java/aimo/backend/domains/privatePost/controller/PrivatePostController.java index 153af22..a594339 100644 --- a/backend/src/main/java/aimo/backend/domains/privatePost/controller/PrivatePostController.java +++ b/backend/src/main/java/aimo/backend/domains/privatePost/controller/PrivatePostController.java @@ -16,30 +16,22 @@ import org.springframework.web.bind.annotation.RestController; import aimo.backend.common.dto.DataResponse; - import aimo.backend.common.util.memberLoader.MemberLoader; import aimo.backend.domains.privatePost.dto.parameter.DeletePrivatePostParameter; import aimo.backend.domains.privatePost.dto.parameter.FindPrivatePostParameter; import aimo.backend.domains.privatePost.dto.parameter.FindPrivatePostPreviewParameter; -import aimo.backend.domains.privatePost.dto.parameter.SpeechToTextParameter; import aimo.backend.domains.privatePost.dto.parameter.JudgementToAiParameter; import aimo.backend.domains.privatePost.dto.request.UpdateContentToPrivatePostRequest; +import aimo.backend.domains.privatePost.dto.request.UploadTextRecordAndRequestJudgementRequest; import aimo.backend.domains.privatePost.dto.response.PrivatePostPreviewResponse; import aimo.backend.domains.privatePost.dto.response.PrivatePostResponse; -import aimo.backend.domains.privatePost.dto.request.SaveAudioSuccessRequest; -import aimo.backend.domains.privatePost.dto.response.SaveAudioSuccessResponse; -import aimo.backend.domains.privatePost.dto.request.SpeechToTextRequest; import aimo.backend.domains.privatePost.dto.response.SpeechToTextResponse; - -import aimo.backend.domains.privatePost.dto.request.UploadTextRecordAndRequestJudgementRequest; import aimo.backend.domains.privatePost.model.OriginType; -import aimo.backend.domains.privatePost.service.AudioRecordService; import aimo.backend.domains.privatePost.service.PrivatePostService; - -import aimo.backend.domains.privatePost.dto.parameter.SaveAudioSuccessParameter; +import aimo.backend.domains.upload.dto.parameter.SpeechToTextParameter; +import aimo.backend.domains.upload.dto.request.SpeechToTextRequest; +import aimo.backend.domains.upload.service.AudioRecordService; import aimo.backend.infrastructure.s3.S3Service; -import aimo.backend.infrastructure.s3.dto.request.CreatePresignedUrlRequest; -import aimo.backend.infrastructure.s3.dto.response.CreatePresignedUrlResponse; import jakarta.validation.Valid; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; @@ -100,26 +92,6 @@ public ResponseEntity> speechToText( .body(DataResponse.created(audioRecordService.speechToText(parameter))); } - @GetMapping("/audio/presigned/{filename}") - public ResponseEntity> getPresignedUrlTo( - @Valid @PathVariable("filename") String filename - ) { - CreatePresignedUrlRequest createPresignedUrlRequest = CreatePresignedUrlRequest.of(filename); - - return ResponseEntity.status(HttpStatus.CREATED) - .body(DataResponse.created(s3Service.createAudioPreSignedUrl(createPresignedUrlRequest))); - } - - @PostMapping("/audio/success") - public ResponseEntity> saveAudioRecord( - @Valid @RequestBody SaveAudioSuccessRequest saveAudioSuccessRequest - ) { - SaveAudioSuccessParameter parameter = SaveAudioSuccessParameter.from(saveAudioSuccessRequest); - - return ResponseEntity.status(HttpStatus.CREATED) - .body(DataResponse.created(audioRecordService.save(parameter))); - } - // 개인글 조회 @GetMapping("/{privatePostId}") public ResponseEntity> findPrivatePost( diff --git a/backend/src/main/java/aimo/backend/domains/privatePost/dto/parameter/SaveAudioSuccessParameter.java b/backend/src/main/java/aimo/backend/domains/privatePost/dto/parameter/SaveAudioSuccessParameter.java deleted file mode 100644 index a04865d..0000000 --- a/backend/src/main/java/aimo/backend/domains/privatePost/dto/parameter/SaveAudioSuccessParameter.java +++ /dev/null @@ -1,20 +0,0 @@ -package aimo.backend.domains.privatePost.dto.parameter; - -import aimo.backend.domains.privatePost.dto.request.SaveAudioSuccessRequest; - -public record SaveAudioSuccessParameter( - String url, - Long size, - String filename, - String extension -) { - - public static SaveAudioSuccessParameter from(SaveAudioSuccessRequest saveAudioSuccessRequest) { - return new SaveAudioSuccessParameter( - saveAudioSuccessRequest.url(), - saveAudioSuccessRequest.size(), - saveAudioSuccessRequest.filename(), - saveAudioSuccessRequest.extension() - ); - } -} diff --git a/backend/src/main/java/aimo/backend/domains/privatePost/dto/request/AudioRecordPresignedRequest.java b/backend/src/main/java/aimo/backend/domains/privatePost/dto/request/AudioRecordPresignedRequest.java deleted file mode 100644 index 2bde0a2..0000000 --- a/backend/src/main/java/aimo/backend/domains/privatePost/dto/request/AudioRecordPresignedRequest.java +++ /dev/null @@ -1,13 +0,0 @@ -package aimo.backend.domains.privatePost.dto.request; - -import jakarta.validation.constraints.NotNull; - -public record AudioRecordPresignedRequest( - @NotNull(message = "파일명이 비었습니다.") - String filename -) { - - public static AudioRecordPresignedRequest of(String filename) { - return new AudioRecordPresignedRequest(filename); - } -} diff --git a/backend/src/main/java/aimo/backend/domains/privatePost/dto/response/AudioRecordPresignedResponse.java b/backend/src/main/java/aimo/backend/domains/privatePost/dto/response/AudioRecordPresignedResponse.java deleted file mode 100644 index 782720f..0000000 --- a/backend/src/main/java/aimo/backend/domains/privatePost/dto/response/AudioRecordPresignedResponse.java +++ /dev/null @@ -1,15 +0,0 @@ -package aimo.backend.domains.privatePost.dto.response; - - -public record AudioRecordPresignedResponse( - String presignedUrl, - String filename -) { - - public static AudioRecordPresignedResponse of( - String presignedUrl, - String filename - ) { - return new AudioRecordPresignedResponse(presignedUrl, filename); - } -} diff --git a/backend/src/main/java/aimo/backend/domains/privatePost/dto/response/SaveAudioSuccessResponse.java b/backend/src/main/java/aimo/backend/domains/privatePost/dto/response/SaveAudioSuccessResponse.java deleted file mode 100644 index cad7157..0000000 --- a/backend/src/main/java/aimo/backend/domains/privatePost/dto/response/SaveAudioSuccessResponse.java +++ /dev/null @@ -1,22 +0,0 @@ -package aimo.backend.domains.privatePost.dto.response; - -import aimo.backend.domains.privatePost.entity.AudioRecord; - -public record SaveAudioSuccessResponse( - String url, - Long size, - String filename -) { - - public static SaveAudioSuccessResponse of(String url, Long size, String filename) { - return new SaveAudioSuccessResponse(url, size, filename); - } - - public static SaveAudioSuccessResponse from(AudioRecord audioRecord) { - return new SaveAudioSuccessResponse( - audioRecord.getUrl(), - audioRecord.getSize(), - audioRecord.getFilename() - ); - } -} diff --git a/backend/src/main/java/aimo/backend/domains/privatePost/entity/AudioRecord.java b/backend/src/main/java/aimo/backend/domains/privatePost/entity/AudioRecord.java index 9e0cc87..5a1e7c9 100644 --- a/backend/src/main/java/aimo/backend/domains/privatePost/entity/AudioRecord.java +++ b/backend/src/main/java/aimo/backend/domains/privatePost/entity/AudioRecord.java @@ -4,7 +4,7 @@ import static lombok.AccessLevel.*; import aimo.backend.common.entity.BaseEntity; -import aimo.backend.domains.privatePost.dto.parameter.SaveAudioSuccessParameter; +import aimo.backend.domains.upload.dto.parameter.SaveAudioMetaDataParameter; import jakarta.persistence.Column; import jakarta.persistence.Entity; import jakarta.persistence.GeneratedValue; @@ -49,7 +49,7 @@ private AudioRecord(String filename, String extension, String url, Long size) { this.extension = extension; } - public static AudioRecord from(SaveAudioSuccessParameter parameter) { + public static AudioRecord from(SaveAudioMetaDataParameter parameter) { return AudioRecord.builder() .filename(parameter.filename()) .extension(parameter.extension()) diff --git a/backend/src/main/java/aimo/backend/domains/privatePost/service/AudioRecordService.java b/backend/src/main/java/aimo/backend/domains/privatePost/service/AudioRecordService.java deleted file mode 100644 index 32b8436..0000000 --- a/backend/src/main/java/aimo/backend/domains/privatePost/service/AudioRecordService.java +++ /dev/null @@ -1,41 +0,0 @@ -package aimo.backend.domains.privatePost.service; - -import org.springframework.stereotype.Service; -import org.springframework.transaction.annotation.Transactional; - -import aimo.backend.common.exception.ApiException; -import aimo.backend.common.properties.AiServerProperties; -import aimo.backend.common.util.webclient.ReactiveHttpService; -import aimo.backend.domains.privatePost.dto.parameter.SaveAudioSuccessParameter; -import aimo.backend.domains.privatePost.dto.parameter.SpeechToTextParameter; -import aimo.backend.domains.privatePost.dto.response.SaveAudioSuccessResponse; -import aimo.backend.domains.privatePost.dto.response.SpeechToTextResponse; -import aimo.backend.domains.privatePost.entity.AudioRecord; -import aimo.backend.domains.privatePost.repository.AudioRecordRepository; - -import lombok.RequiredArgsConstructor; - -@Service -@Transactional(readOnly = true) -@RequiredArgsConstructor -public class AudioRecordService { - - private final AudioRecordRepository audioRecordRepository; - private final ReactiveHttpService reactiveHttpService; - private final AiServerProperties aiServerProperties; - - public SpeechToTextResponse speechToText(SpeechToTextParameter speechToTextParameter) { - String url = aiServerProperties.getDomainUrl() + aiServerProperties.getSpeechToTextApi(); - return reactiveHttpService.post(url, speechToTextParameter).block(); - } - - @Transactional(rollbackFor = ApiException.class) - public SaveAudioSuccessResponse save(SaveAudioSuccessParameter parameter) { - AudioRecord audioRecord = audioRecordRepository.save(AudioRecord.from(parameter)); - return SaveAudioSuccessResponse.from(audioRecord); - } - - private boolean isAudioFile(String extension) { - return extension.equals("mp3") || extension.equals("wav") || extension.equals("ogg") || extension.equals("acc") || extension.equals("flac") || extension.equals("m4a"); - } -} diff --git a/backend/src/main/java/aimo/backend/domains/upload/controller/AudioController.java b/backend/src/main/java/aimo/backend/domains/upload/controller/AudioController.java new file mode 100644 index 0000000..d26930e --- /dev/null +++ b/backend/src/main/java/aimo/backend/domains/upload/controller/AudioController.java @@ -0,0 +1,50 @@ +package aimo.backend.domains.upload.controller; + +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.GetMapping; +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.RestController; + +import aimo.backend.common.dto.DataResponse; +import aimo.backend.domains.upload.dto.parameter.CreateAudioPreSignedUrlParameter; +import aimo.backend.domains.upload.dto.parameter.SaveAudioMetaDataParameter; +import aimo.backend.domains.upload.dto.request.SaveAudioMetaDataRequest; +import aimo.backend.domains.upload.dto.response.SaveAudioMetaDataResponse; +import aimo.backend.domains.upload.service.AudioRecordService; +import aimo.backend.infrastructure.s3.dto.request.CreatePreSignedUrlRequest; +import aimo.backend.infrastructure.s3.dto.response.CreatePreSignedUrlResponse; +import jakarta.validation.Valid; +import lombok.RequiredArgsConstructor; + +@RestController +@RequiredArgsConstructor +@RequestMapping("/api/v1/audio") +public class AudioController { + + private final AudioRecordService audioRecordService; + + @PostMapping + public ResponseEntity> saveAudioMetaData( + @Valid @RequestBody SaveAudioMetaDataRequest request + ) { + SaveAudioMetaDataParameter parameter = SaveAudioMetaDataParameter.from(request); + + return ResponseEntity.status(HttpStatus.CREATED) + .body(DataResponse.created(audioRecordService.save(parameter))); + } + + @GetMapping("/presigned/{filename}") + public ResponseEntity> createAudioPreSignedUrl( + @Valid @RequestBody CreatePreSignedUrlRequest request + ) { + CreateAudioPreSignedUrlParameter parameter = CreateAudioPreSignedUrlParameter.of(request.filename()); + + CreatePreSignedUrlResponse response = audioRecordService.createAudioPreSignedUrl(parameter); + + return ResponseEntity.status(HttpStatus.CREATED) + .body(DataResponse.created(response)); + } +} diff --git a/backend/src/main/java/aimo/backend/domains/upload/dto/parameter/CreateAudioPreSignedUrlParameter.java b/backend/src/main/java/aimo/backend/domains/upload/dto/parameter/CreateAudioPreSignedUrlParameter.java new file mode 100644 index 0000000..6356232 --- /dev/null +++ b/backend/src/main/java/aimo/backend/domains/upload/dto/parameter/CreateAudioPreSignedUrlParameter.java @@ -0,0 +1,10 @@ +package aimo.backend.domains.upload.dto.parameter; + +public record CreateAudioPreSignedUrlParameter(String filename, String extension) { + + public static CreateAudioPreSignedUrlParameter of(String filename) { + String extension = filename.substring(filename.lastIndexOf(".") + 1); + + return new CreateAudioPreSignedUrlParameter(filename, extension); + } +} diff --git a/backend/src/main/java/aimo/backend/domains/upload/dto/parameter/SaveAudioMetaDataParameter.java b/backend/src/main/java/aimo/backend/domains/upload/dto/parameter/SaveAudioMetaDataParameter.java new file mode 100644 index 0000000..513cd53 --- /dev/null +++ b/backend/src/main/java/aimo/backend/domains/upload/dto/parameter/SaveAudioMetaDataParameter.java @@ -0,0 +1,20 @@ +package aimo.backend.domains.upload.dto.parameter; + +import aimo.backend.domains.upload.dto.request.SaveAudioMetaDataRequest; + +public record SaveAudioMetaDataParameter( + String url, + Long size, + String filename, + String extension +) { + + public static SaveAudioMetaDataParameter from(SaveAudioMetaDataRequest request) { + return new SaveAudioMetaDataParameter( + request.url(), + request.size(), + request.filename(), + request.extension() + ); + } +} diff --git a/backend/src/main/java/aimo/backend/domains/privatePost/dto/parameter/SpeechToTextParameter.java b/backend/src/main/java/aimo/backend/domains/upload/dto/parameter/SpeechToTextParameter.java similarity index 68% rename from backend/src/main/java/aimo/backend/domains/privatePost/dto/parameter/SpeechToTextParameter.java rename to backend/src/main/java/aimo/backend/domains/upload/dto/parameter/SpeechToTextParameter.java index a208524..479b233 100644 --- a/backend/src/main/java/aimo/backend/domains/privatePost/dto/parameter/SpeechToTextParameter.java +++ b/backend/src/main/java/aimo/backend/domains/upload/dto/parameter/SpeechToTextParameter.java @@ -1,6 +1,6 @@ -package aimo.backend.domains.privatePost.dto.parameter; +package aimo.backend.domains.upload.dto.parameter; -import aimo.backend.domains.privatePost.dto.request.SpeechToTextRequest; +import aimo.backend.domains.upload.dto.request.SpeechToTextRequest; public record SpeechToTextParameter(String url) { diff --git a/backend/src/main/java/aimo/backend/domains/privatePost/dto/request/SaveAudioSuccessRequest.java b/backend/src/main/java/aimo/backend/domains/upload/dto/request/SaveAudioMetaDataRequest.java similarity index 56% rename from backend/src/main/java/aimo/backend/domains/privatePost/dto/request/SaveAudioSuccessRequest.java rename to backend/src/main/java/aimo/backend/domains/upload/dto/request/SaveAudioMetaDataRequest.java index 87a53ae..a4dcd9a 100644 --- a/backend/src/main/java/aimo/backend/domains/privatePost/dto/request/SaveAudioSuccessRequest.java +++ b/backend/src/main/java/aimo/backend/domains/upload/dto/request/SaveAudioMetaDataRequest.java @@ -1,8 +1,8 @@ -package aimo.backend.domains.privatePost.dto.request; +package aimo.backend.domains.upload.dto.request; import jakarta.validation.constraints.NotNull; -public record SaveAudioSuccessRequest( +public record SaveAudioMetaDataRequest( @NotNull(message = "URL이 필요합니다.") String url, @NotNull(message = "파일 사이즈가 필요합니다.") @@ -13,7 +13,7 @@ public record SaveAudioSuccessRequest( String extension ) { - public static SaveAudioSuccessRequest of(String url, Long size, String filename, String extension) { - return new SaveAudioSuccessRequest(url, size, filename, extension); + public static SaveAudioMetaDataRequest of(String url, Long size, String filename, String extension) { + return new SaveAudioMetaDataRequest(url, size, filename, extension); } } diff --git a/backend/src/main/java/aimo/backend/domains/privatePost/dto/request/SpeechToTextRequest.java b/backend/src/main/java/aimo/backend/domains/upload/dto/request/SpeechToTextRequest.java similarity index 81% rename from backend/src/main/java/aimo/backend/domains/privatePost/dto/request/SpeechToTextRequest.java rename to backend/src/main/java/aimo/backend/domains/upload/dto/request/SpeechToTextRequest.java index f14bf62..7be8dcf 100644 --- a/backend/src/main/java/aimo/backend/domains/privatePost/dto/request/SpeechToTextRequest.java +++ b/backend/src/main/java/aimo/backend/domains/upload/dto/request/SpeechToTextRequest.java @@ -1,4 +1,4 @@ -package aimo.backend.domains.privatePost.dto.request; +package aimo.backend.domains.upload.dto.request; import jakarta.validation.constraints.NotNull; diff --git a/backend/src/main/java/aimo/backend/domains/upload/dto/response/SaveAudioMetaDataResponse.java b/backend/src/main/java/aimo/backend/domains/upload/dto/response/SaveAudioMetaDataResponse.java new file mode 100644 index 0000000..d180d0c --- /dev/null +++ b/backend/src/main/java/aimo/backend/domains/upload/dto/response/SaveAudioMetaDataResponse.java @@ -0,0 +1,22 @@ +package aimo.backend.domains.upload.dto.response; + +import aimo.backend.domains.privatePost.entity.AudioRecord; + +public record SaveAudioMetaDataResponse( + String url, + Long size, + String filename +) { + + public static SaveAudioMetaDataResponse of(String url, Long size, String filename) { + return new SaveAudioMetaDataResponse(url, size, filename); + } + + public static SaveAudioMetaDataResponse from(AudioRecord audioRecord) { + return new SaveAudioMetaDataResponse( + audioRecord.getUrl(), + audioRecord.getSize(), + audioRecord.getFilename() + ); + } +} diff --git a/backend/src/main/java/aimo/backend/domains/upload/service/AudioRecordService.java b/backend/src/main/java/aimo/backend/domains/upload/service/AudioRecordService.java new file mode 100644 index 0000000..af2a166 --- /dev/null +++ b/backend/src/main/java/aimo/backend/domains/upload/service/AudioRecordService.java @@ -0,0 +1,65 @@ +package aimo.backend.domains.upload.service; + +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import aimo.backend.common.exception.ApiException; +import aimo.backend.common.exception.ErrorCode; +import aimo.backend.common.properties.AiServerProperties; +import aimo.backend.common.util.webclient.ReactiveHttpService; +import aimo.backend.domains.upload.dto.parameter.CreateAudioPreSignedUrlParameter; +import aimo.backend.domains.upload.dto.parameter.SaveAudioMetaDataParameter; +import aimo.backend.domains.upload.dto.parameter.SpeechToTextParameter; +import aimo.backend.domains.upload.dto.response.SaveAudioMetaDataResponse; +import aimo.backend.domains.privatePost.dto.response.SpeechToTextResponse; +import aimo.backend.domains.privatePost.entity.AudioRecord; +import aimo.backend.domains.privatePost.repository.AudioRecordRepository; + +import aimo.backend.infrastructure.s3.S3Service; +import aimo.backend.infrastructure.s3.dto.response.CreatePreSignedUrlResponse; +import aimo.backend.infrastructure.s3.model.PresignedUrlPrefix; +import lombok.RequiredArgsConstructor; + +@Service +@Transactional(readOnly = true) +@RequiredArgsConstructor +public class AudioRecordService { + + private final AudioRecordRepository audioRecordRepository; + private final ReactiveHttpService reactiveHttpService; + private final AiServerProperties aiServerProperties; + private final S3Service s3Service; + + // AI서버에 음성 파일을 텍스트로 변환 요청 + public SpeechToTextResponse speechToText(SpeechToTextParameter speechToTextParameter) { + String url = aiServerProperties.getDomainUrl() + aiServerProperties.getSpeechToTextApi(); + return reactiveHttpService.post(url, speechToTextParameter).block(); + } + + // 음성 파일 메타데이터 저장 + @Transactional(rollbackFor = ApiException.class) + public SaveAudioMetaDataResponse save(SaveAudioMetaDataParameter parameter) { + AudioRecord audioRecord = audioRecordRepository.save(AudioRecord.from(parameter)); + return SaveAudioMetaDataResponse.from(audioRecord); + } + + public CreatePreSignedUrlResponse createAudioPreSignedUrl(CreateAudioPreSignedUrlParameter parameter) { + if (!isAudioFile(parameter.extension())) { + throw ApiException.from(ErrorCode.INVALID_FILE_EXTENSION); + } + + return s3Service.createPreSignedUrl( + parameter.filename(), + PresignedUrlPrefix.AUDIO + ); + } + + private boolean isAudioFile(String extension) { + return extension.equals("mp3") + || extension.equals("wav") + || extension.equals("ogg") + || extension.equals("acc") + || extension.equals("flac") + || extension.equals("m4a"); + } +} diff --git a/backend/src/main/java/aimo/backend/infrastructure/s3/S3Service.java b/backend/src/main/java/aimo/backend/infrastructure/s3/S3Service.java index 46a4bbb..fe1d238 100644 --- a/backend/src/main/java/aimo/backend/infrastructure/s3/S3Service.java +++ b/backend/src/main/java/aimo/backend/infrastructure/s3/S3Service.java @@ -11,8 +11,7 @@ import aimo.backend.common.properties.S3Properties; import aimo.backend.infrastructure.s3.dto.parameter.CreateResourceUrlParameter; -import aimo.backend.infrastructure.s3.dto.request.CreatePresignedUrlRequest; -import aimo.backend.infrastructure.s3.dto.response.CreatePresignedUrlResponse; +import aimo.backend.infrastructure.s3.dto.response.CreatePreSignedUrlResponse; import aimo.backend.infrastructure.s3.model.PresignedUrlPrefix; import lombok.RequiredArgsConstructor; @@ -23,19 +22,18 @@ public class S3Service { private final AmazonS3 amazonS3Client; private final S3Properties s3Properties; - public CreatePresignedUrlResponse createAudioPreSignedUrl(CreatePresignedUrlRequest request) { - String path = createPath(PresignedUrlPrefix.AUDIO.getValue(), request.filename()); - String url = createGeneratePresignedUrlRequest(path); - return new CreatePresignedUrlResponse(url, request.filename()); + // PreSignedUrl 생성 + public CreatePreSignedUrlResponse createPreSignedUrl( + String filename, + PresignedUrlPrefix prefix + ) { + String path = createPath(prefix, filename); + String url = createGeneratePreSignedUrlRequest(path); + return new CreatePreSignedUrlResponse(url, filename); } - public CreatePresignedUrlResponse createProfilePresignedUrl(CreatePresignedUrlRequest request) { - String path = createPath(PresignedUrlPrefix.IMAGE.getValue(), request.filename()); - String url = createGeneratePresignedUrlRequest(path); - return new CreatePresignedUrlResponse(url, request.filename()); - } - - private String createGeneratePresignedUrlRequest(String path) { + // s3를 통해 PreSignedUrl 생성 + private String createGeneratePreSignedUrlRequest(String path) { GeneratePresignedUrlRequest generatePresignedUrlRequest = new GeneratePresignedUrlRequest( s3Properties.getBucketName(), path) .withMethod(HttpMethod.PUT) @@ -43,11 +41,14 @@ private String createGeneratePresignedUrlRequest(String path) { return amazonS3Client.generatePresignedUrl(generatePresignedUrlRequest).toString(); } + // 파일 URL 생성 public String getResourceUrl(CreateResourceUrlParameter createResourceUrlParameter) { - String key = createResourceUrlParameter.prefix() + "/" + createResourceUrlParameter.filename() + "." + createResourceUrlParameter.extension(); + String key = createResourceUrlParameter.prefix() + "/" + createResourceUrlParameter.filename() + "." + + createResourceUrlParameter.extension(); return amazonS3Client.getUrl(s3Properties.getBucketName(), key).toString(); } + // PreSignedUrl 만료 시간 설정 private Date getPreSignedUrlExpiration() { Date expiration = new Date(); long expTimeMillis = expiration.getTime(); @@ -65,13 +66,8 @@ private String createFileId() { return UUID.randomUUID().toString(); } - /** - * 파일의 전체 경로를 생성 - * - * @param prefix 디렉토리 경로 - * @return 파일의 전체 경로 - */ - private String createPath(String prefix, String fileName) { - return String.format("%s/%s", prefix, fileName); + // 파일 형식 및 이름으로 저장 + private String createPath(PresignedUrlPrefix prefix, String fileName) { + return String.format("%s/%s", prefix.getValue(), fileName); } } diff --git a/backend/src/main/java/aimo/backend/infrastructure/s3/dto/request/CreatePreSignedUrlRequest.java b/backend/src/main/java/aimo/backend/infrastructure/s3/dto/request/CreatePreSignedUrlRequest.java new file mode 100644 index 0000000..8df722a --- /dev/null +++ b/backend/src/main/java/aimo/backend/infrastructure/s3/dto/request/CreatePreSignedUrlRequest.java @@ -0,0 +1,10 @@ +package aimo.backend.infrastructure.s3.dto.request; + +public record CreatePreSignedUrlRequest( + String filename +) { + + public static CreatePreSignedUrlRequest of(String filename) { + return new CreatePreSignedUrlRequest(filename); + } +} diff --git a/backend/src/main/java/aimo/backend/infrastructure/s3/dto/request/CreatePresignedUrlRequest.java b/backend/src/main/java/aimo/backend/infrastructure/s3/dto/request/CreatePresignedUrlRequest.java deleted file mode 100644 index 1585a05..0000000 --- a/backend/src/main/java/aimo/backend/infrastructure/s3/dto/request/CreatePresignedUrlRequest.java +++ /dev/null @@ -1,10 +0,0 @@ -package aimo.backend.infrastructure.s3.dto.request; - -public record CreatePresignedUrlRequest( - String filename -) { - - public static CreatePresignedUrlRequest of(String filename) { - return new CreatePresignedUrlRequest(filename); - } -} diff --git a/backend/src/main/java/aimo/backend/infrastructure/s3/dto/response/CreatePresignedUrlResponse.java b/backend/src/main/java/aimo/backend/infrastructure/s3/dto/response/CreatePresignedUrlResponse.java index 56ff965..759aa31 100644 --- a/backend/src/main/java/aimo/backend/infrastructure/s3/dto/response/CreatePresignedUrlResponse.java +++ b/backend/src/main/java/aimo/backend/infrastructure/s3/dto/response/CreatePresignedUrlResponse.java @@ -1,13 +1,11 @@ package aimo.backend.infrastructure.s3.dto.response; -import aimo.backend.infrastructure.s3.dto.request.CreatePresignedUrlRequest; - -public record CreatePresignedUrlResponse( - String presignedUrl, +public record CreatePreSignedUrlResponse( + String preSignedUrl, String filename ) { - public static CreatePresignedUrlResponse of(String presignedUrl, String filename) { - return new CreatePresignedUrlResponse(presignedUrl, filename); + public static CreatePreSignedUrlResponse of(String preSignedUrl, String filename) { + return new CreatePreSignedUrlResponse(preSignedUrl, filename); } } diff --git a/backend/src/main/java/aimo/backend/infrastructure/s3/model/PresignedUrlPrefix.java b/backend/src/main/java/aimo/backend/infrastructure/s3/model/PresignedUrlPrefix.java index d7a76ff..a46dc4b 100644 --- a/backend/src/main/java/aimo/backend/infrastructure/s3/model/PresignedUrlPrefix.java +++ b/backend/src/main/java/aimo/backend/infrastructure/s3/model/PresignedUrlPrefix.java @@ -7,7 +7,9 @@ @RequiredArgsConstructor public enum PresignedUrlPrefix { - IMAGE("image"), AUDIO("audio"), + IMAGE("image"), + AUDIO("audio"), + PROFILE("profile"), ; private final String value; }