diff --git a/README.md b/README.md index 9e9b9e2..0b96aa7 100644 --- a/README.md +++ b/README.md @@ -6,6 +6,7 @@ ## ⚡️변경사항 확인 바람⚡️ +- [Entity를 변경하고 다시 시작해도, db에 반영이 안되는 이유](https://github.com/UMC-CodePlay/CodePlay-BE/discussions/125) - [배포 과정 설명](https://github.com/UMC-CodePlay/CodePlay-BE/discussions/52) - [Docker를 이용한 DB 세팅 방법 확인하기](https://github.com/UMC-CodePlay/CodePlay-BE/discussions/28) - [자동 스타일 포맷터 사용법 바로가기](https://github.com/UMC-CodePlay/CodePlay-BE/discussions/3#discussioncomment-11796830) diff --git a/docker/local/deleteEvent.sql b/docker/local/deleteEvent.sql new file mode 100644 index 0000000..7f31c71 --- /dev/null +++ b/docker/local/deleteEvent.sql @@ -0,0 +1,20 @@ +create event clean_input_data on schedule + EVERY 1 DAY STARTS CURRENT_DATE + INTERVAL 1 DAY + enable + do + BEGIN + -- 2달 이상 지난 데이터 삭제 + DELETE FROM music WHERE DATE(created_at) < DATE_SUB(NOW(), INTERVAL 60 DAY); + DELETE FROM remix WHERE DATE(updated_at) < DATE_SUB(NOW(), INTERVAL 60 DAY); + END; + +create event clean_result_data on schedule + EVERY 1 DAY STARTS CURRENT_DATE + INTERVAL 1 DAY + enable + do + BEGIN + -- 2주 이상 지난 데이터 삭제 + DELETE FROM harmony WHERE DATE(updated_at) < DATE_SUB(NOW(), INTERVAL 14 DAY); + DELETE FROM track WHERE DATE(updated_at) < DATE_SUB(NOW(), INTERVAL 14 DAY); + UPDATE remix set deleted_at = now() WHERE DATE(updated_at) < DATE_SUB(NOW(), INTERVAL 14 DAY); + END; \ No newline at end of file diff --git a/src/main/java/umc/codeplay/apiPayLoad/code/status/ErrorStatus.java b/src/main/java/umc/codeplay/apiPayLoad/code/status/ErrorStatus.java index 85ba965..c1d32e5 100644 --- a/src/main/java/umc/codeplay/apiPayLoad/code/status/ErrorStatus.java +++ b/src/main/java/umc/codeplay/apiPayLoad/code/status/ErrorStatus.java @@ -37,7 +37,12 @@ public enum ErrorStatus implements BaseErrorCode { LIKE_ALREADY_EXIST(HttpStatus.BAD_REQUEST, "LIKE401", "이미 좋아요 목록에 추가된 음원입니다."), EMAIL_SEND_ERROR(HttpStatus.BAD_REQUEST, "EMAIL400", "메일 발송에 실패하였습니다."), - EMAIL_CODE_ERROR(HttpStatus.BAD_REQUEST, "EMAIL401", "유효한 코드가 아닙니다."); + EMAIL_CODE_ERROR(HttpStatus.BAD_REQUEST, "EMAIL401", "유효한 코드가 아닙니다."), + + TASK_NOT_FOUND(HttpStatus.BAD_REQUEST, "TASK400", "해당 task를 찾을 수 없습니다."), + HARMONY_NOT_FOUND(HttpStatus.BAD_REQUEST, "HARMONY400", "해당 화성 분석 결과를 찾을 수 없습니다."), + TRACK_NOT_FOUND(HttpStatus.BAD_REQUEST, "TRACK400", "해당 트랙 분리 결과를 찾을 수 없습니다."), + REMIX_NOT_FOUND(HttpStatus.BAD_REQUEST, "REMIX400", "해당 리믹스 결과를 찾을 수 없습니다."); private final HttpStatus httpStatus; private final String code; diff --git a/src/main/java/umc/codeplay/config/SecurityConfig.java b/src/main/java/umc/codeplay/config/SecurityConfig.java index 7e54f03..604c77e 100644 --- a/src/main/java/umc/codeplay/config/SecurityConfig.java +++ b/src/main/java/umc/codeplay/config/SecurityConfig.java @@ -64,6 +64,7 @@ public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Excepti "/health", "/health/s3", "/auth/**", + "/member/**", "/v2/api-docs", "/v3/api-docs", "/v3/api-docs/**", diff --git a/src/main/java/umc/codeplay/controller/AuthController.java b/src/main/java/umc/codeplay/controller/AuthController.java index a12c387..0a22d2f 100644 --- a/src/main/java/umc/codeplay/controller/AuthController.java +++ b/src/main/java/umc/codeplay/controller/AuthController.java @@ -46,7 +46,7 @@ public ApiResponse login( throw new GeneralHandler(ErrorStatus.AUTHORIZATION_METHOD_ERROR); } - // 아이디/비밀번호를 사용해 AuthenticationToken 생성 + // 이메일/비밀번호를 사용해 AuthenticationToken 생성 UsernamePasswordAuthenticationToken authToken = new UsernamePasswordAuthenticationToken(request.getEmail(), request.getPassword()); @@ -56,7 +56,6 @@ public ApiResponse login( // Role 정보 가져오기 Collection authorities = authentication.getAuthorities(); - // 인증에 성공했다면, JWT 토큰 생성 후 반환 String token = jwtUtil.generateToken(authentication.getName(), authorities); String refreshToken = @@ -86,7 +85,7 @@ public ApiResponse refresh( if (jwtUtil.validateToken(refreshToken) && (jwtUtil.getTypeFromToken(refreshToken).equals("refresh"))) { // 리프레시 토큰에서 사용자명 추출 - String usernameFromToken = jwtUtil.getUsernameFromToken(refreshToken); + String usernameFromToken = jwtUtil.getEmailFromToken(refreshToken); if (!email.equals(usernameFromToken)) { throw new GeneralHandler(ErrorStatus.INVALID_REFRESH_TOKEN); diff --git a/src/main/java/umc/codeplay/controller/FileController.java b/src/main/java/umc/codeplay/controller/FileController.java index 76877af..d265b2b 100644 --- a/src/main/java/umc/codeplay/controller/FileController.java +++ b/src/main/java/umc/codeplay/controller/FileController.java @@ -9,6 +9,7 @@ import lombok.RequiredArgsConstructor; import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; import umc.codeplay.apiPayLoad.ApiResponse; import umc.codeplay.domain.enums.FileType; import umc.codeplay.dto.FileResponseDTO; @@ -19,6 +20,7 @@ @RestController @RequestMapping("/files") @RequiredArgsConstructor +@Tag(name = "file-controller", description = "모든 파일(사진, 음성)을 s3에 올릴 때 사용하는 api") public class FileController { private final FileService fileService; diff --git a/src/main/java/umc/codeplay/controller/MemberController.java b/src/main/java/umc/codeplay/controller/MemberController.java index 2e01640..ff383a6 100644 --- a/src/main/java/umc/codeplay/controller/MemberController.java +++ b/src/main/java/umc/codeplay/controller/MemberController.java @@ -1,16 +1,23 @@ package umc.codeplay.controller; +import java.util.List; +import java.util.Optional; + import org.springframework.security.core.annotation.AuthenticationPrincipal; import org.springframework.web.bind.annotation.*; import lombok.RequiredArgsConstructor; +import io.swagger.v3.oas.annotations.Operation; import umc.codeplay.apiPayLoad.ApiResponse; import umc.codeplay.config.security.CustomUserDetails; import umc.codeplay.converter.MemberConverter; import umc.codeplay.domain.Member; +import umc.codeplay.domain.Music; import umc.codeplay.dto.MemberRequestDTO; import umc.codeplay.dto.MemberResponseDTO; +import umc.codeplay.repository.MemberRepository; +import umc.codeplay.repository.MusicRepository; import umc.codeplay.service.MemberService; @RestController @@ -20,7 +27,10 @@ public class MemberController { private final MemberService memberService; private final MemberConverter memberConverter; + private final MemberRepository memberRepository; + private final MusicRepository musicRepository; + @Operation(summary = "유저 정보 업데이트(비밀번호 변경)", description = "기존 비밀번호, 새로운 비밀번호 입력") @PutMapping("/update") public ApiResponse updateMember( @AuthenticationPrincipal @@ -28,10 +38,114 @@ public ApiResponse updateMember( userDetails, // 현재 로그인된 사용자 정보, email로 조회하기 위해 customUserDetails 사용 @RequestBody MemberRequestDTO.UpdateMemberDTO requestDto) { + // userDetails.getUsername() -> 로그인된 사용자의 email Member updatedMember = memberService.updateMember(userDetails.getUsername(), requestDto); MemberResponseDTO.UpdateResultDTO responseDto = memberConverter.toUpdateResultDTO(updatedMember); return ApiResponse.onSuccess(responseDto); } + + @Operation(summary = "마이페이지 화성분석 탭 결과", description = "로그인한 상태에서 마이페이지") + @GetMapping("/mypage/harmony") + public ApiResponse> getMyHarmony( + @AuthenticationPrincipal CustomUserDetails userDetails) { + + String email = userDetails.getUsername(); + + Optional optionalMember = memberRepository.findByEmail(email); + if (optionalMember.isEmpty()) { + throw new RuntimeException("사용자를 찾을 수 없습니다."); + } + Member member = optionalMember.get(); + + List results = memberService.getMyHarmony(member); + + return ApiResponse.onSuccess(results); + } + + @Operation(summary = "마이페이지 세션분리 탭 결과", description = "로그인한 상태에서 마이페이지") + @GetMapping("/mypage/track") + public ApiResponse> getMyTracks( + @AuthenticationPrincipal CustomUserDetails userDetails) { + + String email = userDetails.getUsername(); + + Optional optionalMember = memberRepository.findByEmail(email); + if (optionalMember.isEmpty()) { + throw new RuntimeException("사용자를 찾을 수 없습니다."); + } + Member member = optionalMember.get(); + + List results = memberService.getMyTrack(member); + + return ApiResponse.onSuccess(results); + } + + @Operation(summary = "마이페이지 전체 검색", description = "파라미터 musicTitle에 음원 제목으로 검색") + @GetMapping("/mypage/search") + public ApiResponse getByMusicTitle( + @AuthenticationPrincipal CustomUserDetails userDetails, + @RequestParam String musicTitle) { + + // 현재 로그인한 사용자 검색 + String email = userDetails.getUsername(); + + Optional optionalMember = memberRepository.findByEmail(email); + if (optionalMember.isEmpty()) { + throw new RuntimeException("사용자를 찾을 수 없습니다."); + } + Member member = optionalMember.get(); + + Music music = musicRepository.findByTitle(musicTitle); + + MemberResponseDTO.GetAllByMusicTitleDTO results = + memberService.getAllByMusicTitle(member, music); + + return ApiResponse.onSuccess(results); + } + + @Operation(summary = "마이페이지 화성분석 검색", description = "파라미터 musicTitle에 음원 제목으로 검색") + @GetMapping("/mypage/harmony/search") + public ApiResponse> getHarmonyByMusicTitle( + @AuthenticationPrincipal CustomUserDetails userDetails, + @RequestParam String musicTitle) { + // 현재 로그인한 사용자 검색 + String email = userDetails.getUsername(); + + Optional optionalMember = memberRepository.findByEmail(email); + if (optionalMember.isEmpty()) { + throw new RuntimeException("사용자를 찾을 수 없습니다."); + } + Member member = optionalMember.get(); + + Music music = musicRepository.findByTitle(musicTitle); + + List results = + memberService.getHarmonyByMusicTitle(member, music); + + return ApiResponse.onSuccess(results); + } + + @Operation(summary = "마이페이지 세션분리 검색", description = "파라미터 musicTitle에 음원 제목으로 검색") + @GetMapping("/mypage/track/search") + public ApiResponse> getTrackByMusicTitle( + @AuthenticationPrincipal CustomUserDetails userDetails, + @RequestParam String musicTitle) { + // 현재 로그인한 사용자 검색 + String email = userDetails.getUsername(); + + Optional optionalMember = memberRepository.findByEmail(email); + if (optionalMember.isEmpty()) { + throw new RuntimeException("사용자를 찾을 수 없습니다."); + } + Member member = optionalMember.get(); + + Music music = musicRepository.findByTitle(musicTitle); + + List results = + memberService.getTrackByMusicTitle(member, music); + + return ApiResponse.onSuccess(results); + } } diff --git a/src/main/java/umc/codeplay/controller/ModelResultController.java b/src/main/java/umc/codeplay/controller/ModelResultController.java new file mode 100644 index 0000000..802dc62 --- /dev/null +++ b/src/main/java/umc/codeplay/controller/ModelResultController.java @@ -0,0 +1,61 @@ +package umc.codeplay.controller; + +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 lombok.RequiredArgsConstructor; + +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; +import umc.codeplay.apiPayLoad.ApiResponse; +import umc.codeplay.dto.ModelRequestDTO; +import umc.codeplay.dto.ModelResponseDTO; +import umc.codeplay.service.ModelService; + +@RestController +@RequiredArgsConstructor +@RequestMapping("/python-model") +@Tag(name = "py-model-controller", description = "Python 로컬 서버의 분석 결과를 저장하는 API (프론트엔드 사용 X)") +public class ModelResultController { + + private final ModelService modelService; + + @Operation( + summary = "화성 분석 결과 저장", + description = "Python 서버의 화성 분석 결과를 저장합니다. 프론트엔드에서 사용하지 않습니다.") + @PostMapping("/harmony") + public ApiResponse updateHarmony( + @RequestBody ModelRequestDTO.HarmonyRequestDTO harmonyRequestDTO) { + + return ApiResponse.onSuccess( + ModelResponseDTO.HarmonyResponseDTO.builder() + .harmonyId(modelService.setHarmony(harmonyRequestDTO)) + .build()); + } + + @Operation( + summary = "세션 분리 결과 저장", + description = "Python 서버의 세션 분리 결과를 저장합니다. 프론트엔드에서 사용하지 않습니다.") + @PostMapping("/tracks") + public ApiResponse updateTrack( + @RequestBody ModelRequestDTO.TrackRequestDTO trackRequestDTO) { + + return ApiResponse.onSuccess( + ModelResponseDTO.TrackResponseDTO.builder() + .trackId(modelService.setTrack(trackRequestDTO)) + .build()); + } + + @Operation(summary = "리믹스 결과 저장", description = "Python 서버의 리믹스 결과를 저장합니다. 프론트엔드에서 사용하지 않습니다.") + @PostMapping("/remix") + public ApiResponse updateTrack( + @RequestBody ModelRequestDTO.RemixRequestDTO remixRequestDTO) { + + return ApiResponse.onSuccess( + ModelResponseDTO.RemixResponseDTO.builder() + .remixId(modelService.setRemix(remixRequestDTO)) + .build()); + } +} diff --git a/src/main/java/umc/codeplay/controller/OAuthController.java b/src/main/java/umc/codeplay/controller/OAuthController.java index 6952339..0924963 100644 --- a/src/main/java/umc/codeplay/controller/OAuthController.java +++ b/src/main/java/umc/codeplay/controller/OAuthController.java @@ -74,12 +74,12 @@ public ApiResponse OAuthCallback( String accessToken = (String) tokenResponse.get("access_token"); Map userInfo = requestOAuthUserInfo(accessToken, properties); String email = null; - String name = null; + // String name = null; switch (provider) { case "google" -> { // (3-a) 구글 UserInfo Endpoint 로 이메일, 프로필 등 조회 email = (String) userInfo.get("email"); - name = (String) userInfo.get("name"); + // name = (String) userInfo.get("name"); } case "kakao" -> { // (3-b) 카카오 UserInfo Endpoint 로 이메일, 프로필 등 조회 @@ -88,14 +88,14 @@ public ApiResponse OAuthCallback( Map kakaoProperties = (Map) userInfo.get("properties"); email = (String) kakaoAccount.get("email"); - name = (String) kakaoProperties.get("nickname"); + // name = (String) kakaoProperties.get("nickname"); } } // (4) 우리 DB에서 회원 조회 or 생성 Member member = memberService.findOrCreateOAuthMember( - email, name, SocialStatus.valueOf(provider.toUpperCase())); + email, SocialStatus.valueOf(provider.toUpperCase())); // (5) JWTUtil 이용해서 Access/Refresh 토큰 발급 var authorities = List.of(new SimpleGrantedAuthority("ROLE_" + member.getRole().name())); diff --git a/src/main/java/umc/codeplay/converter/MemberConverter.java b/src/main/java/umc/codeplay/converter/MemberConverter.java index 75c735c..f9fdc27 100644 --- a/src/main/java/umc/codeplay/converter/MemberConverter.java +++ b/src/main/java/umc/codeplay/converter/MemberConverter.java @@ -2,19 +2,24 @@ import org.springframework.stereotype.Component; +import lombok.RequiredArgsConstructor; + +import umc.codeplay.domain.Harmony; import umc.codeplay.domain.Member; +import umc.codeplay.domain.Track; import umc.codeplay.domain.enums.Role; import umc.codeplay.domain.enums.SocialStatus; import umc.codeplay.dto.MemberRequestDTO; import umc.codeplay.dto.MemberResponseDTO; +import umc.codeplay.service.LikeService; @Component +@RequiredArgsConstructor public class MemberConverter { public static Member toMember(MemberRequestDTO.JoinDto request) { return Member.builder() - .name(request.getName()) .email(request.getEmail()) .password(request.getPassword()) .role(Role.USER) @@ -39,7 +44,41 @@ public static MemberResponseDTO.LoginResultDTO toLoginResultDTO( public static MemberResponseDTO.UpdateResultDTO toUpdateResultDTO(Member member) { return MemberResponseDTO.UpdateResultDTO.builder() .email(member.getEmail()) - .profileUrl(member.getProfileUrl()) + // .profileUrl(member.getProfileUrl()) + .build(); + } + + private final LikeService likeService; // LikeService 주입 + + public MemberResponseDTO.GetMyHarmonyDTO toGetMyHarmonyDTO(Harmony harmony, Member member) { + return MemberResponseDTO.GetMyHarmonyDTO.builder() + .harmonyId(harmony.getId()) + .musicId(harmony.getMusic().getId()) + .musicTitle(harmony.getTitle()) + .createdAt(harmony.getCreatedAt()) + .scale(harmony.getScale()) + .genre(harmony.getGenre()) + .bpm(harmony.getBpm()) + .voiceColor(harmony.getVoiceColor()) + .isLiked( + likeService.isLikedByUser( + member, harmony.getMusic())) // LikeService에서 좋아요 여부 확인 + .build(); + } + + public MemberResponseDTO.GetMyTrackDTO toGetMyTrackDTO(Track track, Member member) { + return MemberResponseDTO.GetMyTrackDTO.builder() + .trackId(track.getId()) + .musicId(track.getMusic().getId()) + .musicTitle(track.getTitle()) + .createdAt(track.getCreatedAt()) + .bassUrl(track.getBassUrl()) + .instrumentalUrl(track.getInstrumentalUrl()) + .bassUrl(track.getBassUrl()) + .drumsUrl(track.getDrumsUrl()) + .isLiked( + likeService.isLikedByUser( + member, track.getMusic())) // LikeService에서 좋아요 여부 확인 .build(); } } diff --git a/src/main/java/umc/codeplay/domain/Harmony.java b/src/main/java/umc/codeplay/domain/Harmony.java index 51018ee..05e4ff5 100644 --- a/src/main/java/umc/codeplay/domain/Harmony.java +++ b/src/main/java/umc/codeplay/domain/Harmony.java @@ -2,37 +2,57 @@ import jakarta.persistence.*; -import lombok.*; +import lombok.AccessLevel; +import lombok.Builder; +import lombok.Getter; +import lombok.NoArgsConstructor; +import org.hibernate.annotations.Comment; import umc.codeplay.domain.common.BaseEntity; @Entity @Getter -@Builder @NoArgsConstructor(access = AccessLevel.PROTECTED) -@AllArgsConstructor public class Harmony extends BaseEntity { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) + // TODO: 추후 BigInteger로 변환 private Long id; - @Column(nullable = false, length = 50) + @Column(nullable = false, length = 100) private String title; - private String harmonyKey; - + @Comment("스케일 = key") private String scale; - private String chord; + @Comment("장르") + private String genre; + @Comment("bpm 값") private Integer bpm; - private Integer soundPressure; - - @Column(columnDefinition = "TEXT") - private String harmonyUrl; + @Comment("음색") + private String voiceColor; @ManyToOne(fetch = FetchType.LAZY) - @JoinColumn(name = "music_id") + @Comment("입력 음악 ID") + @JoinColumn(name = "music_id", nullable = false) private Music music; + + @Builder + private Harmony(String scale, String genre, Integer bpm, String voiceColor, Music music) { + this.title = music.getTitle().split("-", 2)[1]; + this.scale = scale; + this.genre = genre; + this.bpm = bpm; + this.voiceColor = voiceColor; + this.music = music; + } + + public void updateHarmonyResult(String scale, Integer bpm, String genre, String voiceColor) { + this.scale = scale; + this.bpm = bpm; + this.genre = genre; + this.voiceColor = voiceColor; + } } diff --git a/src/main/java/umc/codeplay/domain/Member.java b/src/main/java/umc/codeplay/domain/Member.java index af6b44b..326ea4b 100644 --- a/src/main/java/umc/codeplay/domain/Member.java +++ b/src/main/java/umc/codeplay/domain/Member.java @@ -7,6 +7,8 @@ import lombok.*; import com.fasterxml.jackson.annotation.JsonIgnore; +import org.hibernate.annotations.OnDelete; +import org.hibernate.annotations.OnDeleteAction; import umc.codeplay.domain.common.BaseEntity; import umc.codeplay.domain.enums.Role; import umc.codeplay.domain.enums.SocialStatus; @@ -22,12 +24,13 @@ public class Member extends BaseEntity { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) + // TODO: 추후 BigInteger로 변환 private Long id; - private String name; - + @Column(nullable = false) private String password; + @Column(nullable = false) private String email; @Enumerated(EnumType.STRING) @@ -36,14 +39,20 @@ public class Member extends BaseEntity { @Enumerated(EnumType.STRING) private SocialStatus socialStatus; - public void encodePassword(String password) { - this.password = password; - } - @Column(columnDefinition = "TEXT") private String profileUrl; @OneToMany(mappedBy = "member", cascade = CascadeType.ALL) + @OnDelete(action = OnDeleteAction.CASCADE) @JsonIgnore private List likeList = new ArrayList<>(); + + @OneToMany(mappedBy = "member", cascade = CascadeType.ALL) + @OnDelete(action = OnDeleteAction.CASCADE) + @JsonIgnore + private List musicList = new ArrayList<>(); + + public void encodePassword(String password) { + this.password = password; + } } diff --git a/src/main/java/umc/codeplay/domain/Music.java b/src/main/java/umc/codeplay/domain/Music.java index 078e509..79e642b 100644 --- a/src/main/java/umc/codeplay/domain/Music.java +++ b/src/main/java/umc/codeplay/domain/Music.java @@ -7,6 +7,8 @@ import lombok.*; import com.fasterxml.jackson.annotation.JsonIgnore; +import org.hibernate.annotations.OnDelete; +import org.hibernate.annotations.OnDeleteAction; import umc.codeplay.domain.common.BaseEntity; import umc.codeplay.domain.mapping.MusicLike; @@ -18,19 +20,36 @@ public class Music extends BaseEntity { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) + // TODO: 추후 BigInteger로 변환 private Long id; - @Column(nullable = false, length = 50) + @Column(nullable = false, length = 100) private String title; + @Column(columnDefinition = "TEXT", nullable = false) + private String musicUrl; + @ManyToOne(fetch = FetchType.LAZY) - @JoinColumn(name = "member_id") + @JoinColumn(name = "member_id", nullable = false) private Member member; - @Column(columnDefinition = "TEXT") - private String musicUrl; - @OneToMany(mappedBy = "music", cascade = CascadeType.ALL) + @OnDelete(action = OnDeleteAction.CASCADE) @JsonIgnore private List likeList = new ArrayList<>(); + + @OneToMany(mappedBy = "music", cascade = CascadeType.ALL) + @OnDelete(action = OnDeleteAction.CASCADE) + @JsonIgnore + private List harmonies = new ArrayList<>(); + + @OneToMany(mappedBy = "music", cascade = CascadeType.ALL) + @OnDelete(action = OnDeleteAction.CASCADE) + @JsonIgnore + private List tracks = new ArrayList<>(); + + @OneToMany(mappedBy = "music", cascade = CascadeType.ALL) + @OnDelete(action = OnDeleteAction.CASCADE) + @JsonIgnore + private List remixes = new ArrayList<>(); } diff --git a/src/main/java/umc/codeplay/domain/Remix.java b/src/main/java/umc/codeplay/domain/Remix.java new file mode 100644 index 0000000..9afe704 --- /dev/null +++ b/src/main/java/umc/codeplay/domain/Remix.java @@ -0,0 +1,79 @@ +package umc.codeplay.domain; + +import java.time.LocalDateTime; +import java.util.ArrayList; +import java.util.List; +import jakarta.persistence.*; + +import lombok.*; + +import com.fasterxml.jackson.annotation.JsonIgnore; +import org.hibernate.annotations.Comment; +import org.hibernate.annotations.OnDelete; +import org.hibernate.annotations.OnDeleteAction; +import umc.codeplay.domain.common.BaseEntity; + +@Entity +@Getter +@NoArgsConstructor(access = AccessLevel.PROTECTED) +public class Remix extends BaseEntity { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + // TODO: 추후 BigInteger로 변환 + private Long id; + + @Column(nullable = false, length = 100) + private String title; + + @Comment("음높이의 변화") + private Integer scaleModulation; + + @Comment("원래 템포에 대한 비율") + private Double tempoRatio; + + @Comment("리버브의 강도/정도") + private Double reverbAmount; + + @Comment("코러스 on/off") + private Boolean isChorusOn; + + @Column(columnDefinition = "TEXT") + @Comment("결과 s3 URL") + @Setter + private String resultMusicUrl; + + private LocalDateTime deletedAt; + + @ManyToOne(fetch = FetchType.LAZY) + @Comment("입력 음악 ID") + @JoinColumn(name = "music_id", nullable = false) + private Music music; + + @ManyToOne(fetch = FetchType.LAZY) + @Comment("이전 단계 리믹스 ID") + @JoinColumn(name = "parent_remix_id") + private Remix parentRemix; + + @OneToMany(mappedBy = "parentRemix", cascade = CascadeType.ALL, orphanRemoval = true) + @OnDelete(action = OnDeleteAction.CASCADE) + @JsonIgnore + private List childRemixList = new ArrayList<>(); + + @Builder + public Remix( + Integer scaleModulation, + Double tempoRatio, + Double reverbAmount, + Boolean isChorusOn, + String resultMusicUrl, + Music music) { + this.title = music.getTitle().split("-", 2)[1]; + this.scaleModulation = scaleModulation; + this.tempoRatio = tempoRatio; + this.reverbAmount = reverbAmount; + this.isChorusOn = isChorusOn; + this.resultMusicUrl = resultMusicUrl; + this.music = music; + } +} diff --git a/src/main/java/umc/codeplay/domain/Task.java b/src/main/java/umc/codeplay/domain/Task.java new file mode 100644 index 0000000..8b64871 --- /dev/null +++ b/src/main/java/umc/codeplay/domain/Task.java @@ -0,0 +1,37 @@ +package umc.codeplay.domain; + +import jakarta.persistence.*; + +import lombok.*; + +import org.hibernate.annotations.Comment; +import umc.codeplay.domain.common.BaseEntity; +import umc.codeplay.domain.enums.JobType; +import umc.codeplay.domain.enums.ProcessStatus; + +@Entity +@Getter +@Builder +@NoArgsConstructor(access = AccessLevel.PROTECTED) +@AllArgsConstructor +public class Task extends BaseEntity { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + // TODO: 추후 BigInteger로 변환 + private Long id; + + @Enumerated(EnumType.STRING) + @Comment("task 진행 상태") + @Builder.Default + private ProcessStatus status = ProcessStatus.REQUESTED; + + @Enumerated(EnumType.STRING) + @Comment("요청 기능 타입 (화성 분석, 세션 분리, 리믹스)") + @Column(nullable = false) + private JobType jobType; + + @Column(nullable = false) + @Comment("요청 기능의 데이터 id") + private Long jobId; +} diff --git a/src/main/java/umc/codeplay/domain/Track.java b/src/main/java/umc/codeplay/domain/Track.java index a211b86..a34ca28 100644 --- a/src/main/java/umc/codeplay/domain/Track.java +++ b/src/main/java/umc/codeplay/domain/Track.java @@ -2,34 +2,64 @@ import jakarta.persistence.*; -import lombok.*; +import lombok.AccessLevel; +import lombok.Builder; +import lombok.Getter; +import lombok.NoArgsConstructor; +import org.hibernate.annotations.Comment; import umc.codeplay.domain.common.BaseEntity; @Entity @Getter -@Builder @NoArgsConstructor(access = AccessLevel.PROTECTED) -@AllArgsConstructor public class Track extends BaseEntity { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) + // TODO: 추후 BigInteger로 변환 private Long id; - @Column(nullable = false, length = 50) + @Column(nullable = false, length = 100) private String title; @Column(columnDefinition = "TEXT") - private String guitarUrl; + @Comment("보컬 url") + private String vocalUrl; @Column(columnDefinition = "TEXT") - private String drumUrl; + @Comment("반주 url") + private String instrumentalUrl; @Column(columnDefinition = "TEXT") - private String keyboardUrl; + @Comment("베이스 url") + private String bassUrl; + + @Column(columnDefinition = "TEXT") + @Comment("드럼 url") + private String drumsUrl; @ManyToOne(fetch = FetchType.LAZY) - @JoinColumn(name = "music_id") + @Comment("입력 음악 ID") + @JoinColumn(name = "music_id", nullable = false) private Music music; + + @Builder + public Track( + String vocalUrl, String instrumentalUrl, String bassUrl, String drumsUrl, Music music) { + this.title = music.getTitle().split("-", 2)[1]; + this.vocalUrl = vocalUrl; + this.instrumentalUrl = instrumentalUrl; + this.bassUrl = bassUrl; + this.drumsUrl = drumsUrl; + this.music = music; + } + + public void updateTrackResult( + String vocalUrl, String instrumentalUrl, String bassUrl, String drumsUrl) { + this.vocalUrl = vocalUrl; + this.instrumentalUrl = instrumentalUrl; + this.bassUrl = bassUrl; + this.drumsUrl = drumsUrl; + } } diff --git a/src/main/java/umc/codeplay/domain/enums/JobType.java b/src/main/java/umc/codeplay/domain/enums/JobType.java new file mode 100644 index 0000000..389fc9d --- /dev/null +++ b/src/main/java/umc/codeplay/domain/enums/JobType.java @@ -0,0 +1,7 @@ +package umc.codeplay.domain.enums; + +public enum JobType { + Harmony, + Track, + Session +} diff --git a/src/main/java/umc/codeplay/domain/enums/ProcessStatus.java b/src/main/java/umc/codeplay/domain/enums/ProcessStatus.java new file mode 100644 index 0000000..e842122 --- /dev/null +++ b/src/main/java/umc/codeplay/domain/enums/ProcessStatus.java @@ -0,0 +1,9 @@ +package umc.codeplay.domain.enums; + +public enum ProcessStatus { + // todo: 그냥 예시로 적어둠 변경 필요 + REQUESTED, + ONGOING, + COMPLETED, + FAILED; +} diff --git a/src/main/java/umc/codeplay/dto/MemberRequestDTO.java b/src/main/java/umc/codeplay/dto/MemberRequestDTO.java index bbcf034..3623637 100644 --- a/src/main/java/umc/codeplay/dto/MemberRequestDTO.java +++ b/src/main/java/umc/codeplay/dto/MemberRequestDTO.java @@ -11,9 +11,6 @@ public class MemberRequestDTO { @Getter public static class JoinDto { - @NotBlank(message = "이름은 필수 입력값입니다.") - String name; - @NotBlank(message = "이메일은 필수 입력값입니다.") @Email(message = "이메일 형식이 아닙니다.") String email; @@ -49,9 +46,17 @@ public static class CheckVerificationCodeDTO { @Setter public static class UpdateMemberDTO { - @NotBlank(message = "비밀번호는 필수 입력값입니다.") - String password; + @NotBlank(message = "기존 비밀번호는 필수 입력값입니다.") + String currentPassword; + + @NotBlank(message = "새로운 비밀번호는 필수 입력값입니다.") + String newPassword; + } + + @Getter + public static class SearchByMusicTitleDTO { - String profileUrl; // 프로필 사진 URL + @NotBlank(message = "음원 제목은 필수 입력값입니다.") + String musicTitle; } } diff --git a/src/main/java/umc/codeplay/dto/MemberResponseDTO.java b/src/main/java/umc/codeplay/dto/MemberResponseDTO.java index 265754f..51100c3 100644 --- a/src/main/java/umc/codeplay/dto/MemberResponseDTO.java +++ b/src/main/java/umc/codeplay/dto/MemberResponseDTO.java @@ -1,5 +1,8 @@ package umc.codeplay.dto; +import java.time.LocalDateTime; +import java.util.List; + import lombok.AllArgsConstructor; import lombok.Builder; import lombok.Getter; @@ -31,6 +34,50 @@ public static class LoginResultDTO { @AllArgsConstructor public static class UpdateResultDTO { String email; - String profileUrl; + } + + @Builder + @Getter + @NoArgsConstructor + @AllArgsConstructor + public static class GetMyHarmonyDTO { + // 화성분석Id, 음원Id, 음원제목, 생성 날짜, 키, bpm, 평균음압, 즐겨찾기 여부 + Long harmonyId; + Long musicId; + String musicTitle; + LocalDateTime createdAt; + String scale; + String genre; + Integer bpm; + String voiceColor; + Boolean isLiked; + } + + @Builder + @Getter + @NoArgsConstructor + @AllArgsConstructor + public static class GetMyTrackDTO { + // 세션 분리 harmonyId, 음원 harmonyId, 음원 제목, 생성일자, 보컬 파일 url, 반주 url, 베이스 파일 url, 드럼 파일 url, + // 즐겨찾기 여부 + Long trackId; + Long musicId; + String musicTitle; + LocalDateTime createdAt; + String vocalUrl; + String instrumentalUrl; + String bassUrl; + String drumsUrl; + Boolean isLiked; + } + + @Builder + @Getter + @NoArgsConstructor + @AllArgsConstructor + public static class GetAllByMusicTitleDTO { + + private List harmonies; + private List tracks; } } diff --git a/src/main/java/umc/codeplay/dto/ModelRequestDTO.java b/src/main/java/umc/codeplay/dto/ModelRequestDTO.java new file mode 100644 index 0000000..eabf3d0 --- /dev/null +++ b/src/main/java/umc/codeplay/dto/ModelRequestDTO.java @@ -0,0 +1,38 @@ +package umc.codeplay.dto; + +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; + +public class ModelRequestDTO { + + @Getter + @NoArgsConstructor + @AllArgsConstructor + public static class HarmonyRequestDTO { + private Long taskId; + private String scale; + private Integer bpm; + private String genre; + private String voiceColor; + } + + @Getter + @NoArgsConstructor + @AllArgsConstructor + public static class TrackRequestDTO { + private Long taskId; + private String vocalUrl; + private String instrumentalUrl; + private String bassUrl; + private String drumsUrl; + } + + @Getter + @NoArgsConstructor + @AllArgsConstructor + public static class RemixRequestDTO { + private Long taskId; + private String resultMusicUrl; + } +} diff --git a/src/main/java/umc/codeplay/dto/ModelResponseDTO.java b/src/main/java/umc/codeplay/dto/ModelResponseDTO.java new file mode 100644 index 0000000..518d9e2 --- /dev/null +++ b/src/main/java/umc/codeplay/dto/ModelResponseDTO.java @@ -0,0 +1,33 @@ +package umc.codeplay.dto; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Getter; +import lombok.NoArgsConstructor; + +public class ModelResponseDTO { + + @Builder + @Getter + @NoArgsConstructor + @AllArgsConstructor + public static class HarmonyResponseDTO { + Long harmonyId; + } + + @Builder + @Getter + @NoArgsConstructor + @AllArgsConstructor + public static class TrackResponseDTO { + Long trackId; + } + + @Builder + @Getter + @NoArgsConstructor + @AllArgsConstructor + public static class RemixResponseDTO { + Long remixId; + } +} diff --git a/src/main/java/umc/codeplay/jwt/JwtAuthenticationFilter.java b/src/main/java/umc/codeplay/jwt/JwtAuthenticationFilter.java index fe2e1b7..08bd439 100644 --- a/src/main/java/umc/codeplay/jwt/JwtAuthenticationFilter.java +++ b/src/main/java/umc/codeplay/jwt/JwtAuthenticationFilter.java @@ -37,9 +37,8 @@ protected void doFilterInternal( if (jwtUtil.validateToken(token) && (jwtUtil.getTypeFromToken(token).equals("access"))) { // 3. 토큰에서 사용자 정보 추출 - String username = jwtUtil.getUsernameFromToken(token); - System.out.println(username); - // String email = jwtUtil.getUsernameFromToken(token); + String email = jwtUtil.getEmailFromToken(token); + System.out.println(email); List roles = jwtUtil.getRolesFromToken(token); List authorities = @@ -48,7 +47,7 @@ protected void doFilterInternal( .collect(Collectors.toList()); // CustomUserDetails 객체 생성 후 저장 - CustomUserDetails userDetails = new CustomUserDetails(username, "", authorities); + CustomUserDetails userDetails = new CustomUserDetails(email, "", authorities); UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(userDetails, null, authorities); diff --git a/src/main/java/umc/codeplay/jwt/JwtUtil.java b/src/main/java/umc/codeplay/jwt/JwtUtil.java index abd5290..b14d628 100644 --- a/src/main/java/umc/codeplay/jwt/JwtUtil.java +++ b/src/main/java/umc/codeplay/jwt/JwtUtil.java @@ -26,8 +26,7 @@ private Key getSigningKey() { } // JWT 토큰 생성 - public String generateToken( - String username, Collection authorities) { + public String generateToken(String email, Collection authorities) { Date now = new Date(); List roleNames = @@ -38,7 +37,7 @@ public String generateToken( // 30분 만료 long EXPIRATION_TIME = 1000 * 60 * 30L; return Jwts.builder() - .setSubject(username) // 사용자 식별 정보 + .setSubject(email) // 사용자 식별 정보 .setIssuedAt(now) .claim("type", "access") .claim("roles", roleNames) // 발급 시간 @@ -48,7 +47,7 @@ public String generateToken( } // JWT 토큰에서 username 추출 - public String getUsernameFromToken(String token) { + public String getEmailFromToken(String token) { return Jwts.parserBuilder() .setSigningKey(getSigningKey()) .build() @@ -94,7 +93,7 @@ public List getRolesFromToken(String token) { // JWT 리프레시 토큰 생성 public String generateRefreshToken( - String username, Collection authorities) { + String email, Collection authorities) { Date now = new Date(); List roleNames = @@ -105,7 +104,7 @@ public String generateRefreshToken( // 1일 만료 long EXPIRATION_TIME = 1000 * 60 * 60 * 24L; return Jwts.builder() - .setSubject(username) // 사용자 식별 정보 + .setSubject(email) // 사용자 식별 정보 .setIssuedAt(now) .claim("type", "refresh") .claim("roles", roleNames) // 역할 정보 추가 diff --git a/src/main/java/umc/codeplay/repository/HarmonyRepository.java b/src/main/java/umc/codeplay/repository/HarmonyRepository.java new file mode 100644 index 0000000..2fa49dd --- /dev/null +++ b/src/main/java/umc/codeplay/repository/HarmonyRepository.java @@ -0,0 +1,27 @@ +package umc.codeplay.repository; + +import java.util.List; + +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Query; +import org.springframework.data.repository.query.Param; + +import umc.codeplay.apiPayLoad.code.status.ErrorStatus; +import umc.codeplay.apiPayLoad.exception.handler.GeneralHandler; +import umc.codeplay.domain.Harmony; +import umc.codeplay.domain.Member; +import umc.codeplay.domain.Music; + +public interface HarmonyRepository extends JpaRepository { + + // 특정 사용자의 Harmony 리스트 조회 + List findByMusicMember(Member member); + + // 특정 사용자의 Harmony 리스트 중 music의 harmony 조회 + @Query("SELECT h FROM Harmony h " + "WHERE h.music = :music AND h.music.member = :member") + List findByMusicAndMember(@Param("member") Member member, @Param("music") Music music); + + default Harmony findByIdOrThrow(Long id) { + return findById(id).orElseThrow(() -> new GeneralHandler(ErrorStatus.HARMONY_NOT_FOUND)); + } +} diff --git a/src/main/java/umc/codeplay/repository/MusicRepository.java b/src/main/java/umc/codeplay/repository/MusicRepository.java index 4ecef4e..5527abd 100644 --- a/src/main/java/umc/codeplay/repository/MusicRepository.java +++ b/src/main/java/umc/codeplay/repository/MusicRepository.java @@ -4,4 +4,7 @@ import umc.codeplay.domain.Music; -public interface MusicRepository extends JpaRepository {} +public interface MusicRepository extends JpaRepository { + // 제목으로 음원 찾기 + Music findByTitle(String title); +} diff --git a/src/main/java/umc/codeplay/repository/RemixRepository.java b/src/main/java/umc/codeplay/repository/RemixRepository.java new file mode 100644 index 0000000..0c2f60f --- /dev/null +++ b/src/main/java/umc/codeplay/repository/RemixRepository.java @@ -0,0 +1,20 @@ +package umc.codeplay.repository; + +import org.springframework.data.jpa.repository.JpaRepository; + +import umc.codeplay.apiPayLoad.code.status.ErrorStatus; +import umc.codeplay.apiPayLoad.exception.handler.GeneralHandler; +import umc.codeplay.domain.Remix; + +public interface RemixRepository extends JpaRepository { + + default Remix findByIdOrThrow(Long id) { + return findById(id).orElseThrow(() -> new GeneralHandler(ErrorStatus.REMIX_NOT_FOUND)); + } + + /* + remix는 60일 동안 보관하지만, 한 remix의 결과가 다른 remix의 input이 되므로 60일 동안 보관하지만, + 다른 기능과 통일을 위해 14일이 지나면 마이페이지에서 보이지 않아야하므로 + TODO: 마이페이지에서 특정 사용자의 Remix 리스트 확인시, deleted_at이 있는 경우를 제외하고 검색 및 조회하기 + */ +} diff --git a/src/main/java/umc/codeplay/repository/TaskRepository.java b/src/main/java/umc/codeplay/repository/TaskRepository.java new file mode 100644 index 0000000..4924a90 --- /dev/null +++ b/src/main/java/umc/codeplay/repository/TaskRepository.java @@ -0,0 +1,14 @@ +package umc.codeplay.repository; + +import org.springframework.data.jpa.repository.JpaRepository; + +import umc.codeplay.apiPayLoad.code.status.ErrorStatus; +import umc.codeplay.apiPayLoad.exception.handler.GeneralHandler; +import umc.codeplay.domain.Task; + +public interface TaskRepository extends JpaRepository { + + default Task findByIdOrThrow(Long id) { + return findById(id).orElseThrow(() -> new GeneralHandler(ErrorStatus.TASK_NOT_FOUND)); + } +} diff --git a/src/main/java/umc/codeplay/repository/TrackRepository.java b/src/main/java/umc/codeplay/repository/TrackRepository.java new file mode 100644 index 0000000..3b65354 --- /dev/null +++ b/src/main/java/umc/codeplay/repository/TrackRepository.java @@ -0,0 +1,26 @@ +package umc.codeplay.repository; + +import java.util.List; + +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Query; +import org.springframework.data.repository.query.Param; + +import umc.codeplay.apiPayLoad.code.status.ErrorStatus; +import umc.codeplay.apiPayLoad.exception.handler.GeneralHandler; +import umc.codeplay.domain.Member; +import umc.codeplay.domain.Music; +import umc.codeplay.domain.Track; + +public interface TrackRepository extends JpaRepository { + // 특정 사용자의 Track 리스트 조회 + List findByMusicMember(Member member); + + // 특정 사용자의 Harmony 리스트 중 music의 harmony 조회 + @Query("SELECT t FROM Track t " + "WHERE t.music = :music AND t.music.member = :member") + List findByMusicAndMember(@Param("member") Member member, @Param("music") Music music); + + default Track findByIdOrThrow(Long id) { + return findById(id).orElseThrow(() -> new GeneralHandler(ErrorStatus.TRACK_NOT_FOUND)); + } +} diff --git a/src/main/java/umc/codeplay/service/LikeService.java b/src/main/java/umc/codeplay/service/LikeService.java index 2fbb080..95f3b3c 100644 --- a/src/main/java/umc/codeplay/service/LikeService.java +++ b/src/main/java/umc/codeplay/service/LikeService.java @@ -66,4 +66,9 @@ public Music removeLike(String username, LikeRequestDTO.removeLikeRequestDTO req musicLikeRepository.delete(musicLike); return music; } + + @Transactional + public boolean isLikedByUser(Member member, Music music) { + return musicLikeRepository.existsByMemberAndMusic(member, music); + } } diff --git a/src/main/java/umc/codeplay/service/MemberService.java b/src/main/java/umc/codeplay/service/MemberService.java index 61f0739..8675514 100644 --- a/src/main/java/umc/codeplay/service/MemberService.java +++ b/src/main/java/umc/codeplay/service/MemberService.java @@ -1,5 +1,8 @@ package umc.codeplay.service; +import java.security.InvalidParameterException; +import java.util.List; +import java.util.stream.Collectors; import jakarta.transaction.Transactional; import org.springframework.security.crypto.password.PasswordEncoder; @@ -10,11 +13,17 @@ import umc.codeplay.apiPayLoad.code.status.ErrorStatus; import umc.codeplay.apiPayLoad.exception.handler.GeneralHandler; import umc.codeplay.converter.MemberConverter; +import umc.codeplay.domain.Harmony; import umc.codeplay.domain.Member; +import umc.codeplay.domain.Music; +import umc.codeplay.domain.Track; import umc.codeplay.domain.enums.Role; import umc.codeplay.domain.enums.SocialStatus; import umc.codeplay.dto.MemberRequestDTO; +import umc.codeplay.dto.MemberResponseDTO; +import umc.codeplay.repository.HarmonyRepository; import umc.codeplay.repository.MemberRepository; +import umc.codeplay.repository.TrackRepository; @Service @RequiredArgsConstructor @@ -22,6 +31,9 @@ public class MemberService { private final MemberRepository memberRepository; private final PasswordEncoder passwordEncoder; + private final HarmonyRepository harmonyRepository; + private final MemberConverter memberConverter; + private final TrackRepository trackRepository; public Member joinMember(MemberRequestDTO.JoinDto request) { @@ -34,7 +46,7 @@ public Member joinMember(MemberRequestDTO.JoinDto request) { return memberRepository.save(newMember); } - public Member findOrCreateOAuthMember(String email, String name, SocialStatus socialStatus) { + public Member findOrCreateOAuthMember(String email, SocialStatus socialStatus) { Member member = memberRepository.findByEmail(email).orElse(null); @@ -42,7 +54,6 @@ public Member findOrCreateOAuthMember(String email, String name, SocialStatus so member = Member.builder() .email(email) - .name(name) .role(Role.USER) .socialStatus(socialStatus) .build(); @@ -65,24 +76,84 @@ public SocialStatus getSocialStatus(String email) { @Transactional public Member updateMember(String email, MemberRequestDTO.UpdateMemberDTO requestDto) { + // MemberRepository의 findByEmail()을 사용하여 회원 조회 Member member = memberRepository .findByEmail(email) .orElseThrow(() -> new IllegalArgumentException("해당 이메일의 회원이 존재하지 않습니다.")); - - // 비밀번호 변경(입력값이 있을 경우만) - if (requestDto.getPassword() != null && !requestDto.getPassword().isEmpty()) { - String encodedPassword = passwordEncoder.encode(requestDto.getPassword()); + System.out.println("console log checking"); + // 사용자 입력 값 + String newPassword = requestDto.getNewPassword(); + String currentPassword = requestDto.getCurrentPassword(); + // 기존 비밀번호 확인 + if (!passwordEncoder.matches(currentPassword, member.getPassword())) { + throw new InvalidParameterException("기존 비밀번호가 일치하지 않습니다."); + // 기존 비밀번호가 일치하고 새로운 비밀번호 입력값이 있을때 비밀번호 변경 + } else if (newPassword != null && !newPassword.isEmpty()) { + String encodedPassword = passwordEncoder.encode(newPassword); member.setPassword(encodedPassword); - } - - // 프로필 사진 변경(입력값이 있을 경우에만) - if (requestDto.getProfileUrl() != null && !requestDto.getProfileUrl().isEmpty()) { - member.setProfileUrl(requestDto.getProfileUrl()); + } else { + throw new InvalidParameterException("새로운 비밀번호를 입력해주세요."); } memberRepository.save(member); return member; } + + public List getMyHarmony(Member member) { + + List harmonies = harmonyRepository.findByMusicMember(member); + + return harmonies.stream() + .map(harmony -> memberConverter.toGetMyHarmonyDTO(harmony, member)) + .collect(Collectors.toList()); + } + + public List getMyTrack(Member member) { + + List tracks = trackRepository.findByMusicMember(member); + + return tracks.stream() + .map(track -> memberConverter.toGetMyTrackDTO(track, member)) + .collect(Collectors.toList()); + } + + public MemberResponseDTO.GetAllByMusicTitleDTO getAllByMusicTitle(Member member, Music music) { + + List harmonies = harmonyRepository.findByMusicAndMember(member, music); + List tracks = trackRepository.findByMusicAndMember(member, music); + + List harmonyDTOs = + harmonies.stream() + .map(harmony -> memberConverter.toGetMyHarmonyDTO(harmony, member)) + .collect(Collectors.toList()); + + List trackDTOs = + tracks.stream() + .map(track -> memberConverter.toGetMyTrackDTO(track, member)) + .collect(Collectors.toList()); + + // DTO를 하나의 객체로 묶어서 반환 + return new MemberResponseDTO.GetAllByMusicTitleDTO(harmonyDTOs, trackDTOs); + } + + // 음원 제목으로 my harmony 검색 + public List getHarmonyByMusicTitle( + Member member, Music music) { + List harmonies = harmonyRepository.findByMusicAndMember(member, music); + + return harmonies.stream() + .map(harmony -> memberConverter.toGetMyHarmonyDTO(harmony, member)) + .collect(Collectors.toList()); + } + + // 음원 제목으로 my track 검색 + public List getTrackByMusicTitle(Member member, Music music) { + List tracks = trackRepository.findByMusicAndMember(member, music); + + return tracks.stream() + .map(track -> memberConverter.toGetMyTrackDTO(track, member)) + .collect(Collectors.toList()); + } } diff --git a/src/main/java/umc/codeplay/service/ModelService.java b/src/main/java/umc/codeplay/service/ModelService.java new file mode 100644 index 0000000..fd797a3 --- /dev/null +++ b/src/main/java/umc/codeplay/service/ModelService.java @@ -0,0 +1,57 @@ +package umc.codeplay.service; + +import org.springframework.stereotype.Service; + +import lombok.RequiredArgsConstructor; + +import umc.codeplay.domain.Harmony; +import umc.codeplay.domain.Remix; +import umc.codeplay.domain.Task; +import umc.codeplay.domain.Track; +import umc.codeplay.dto.ModelRequestDTO; +import umc.codeplay.repository.HarmonyRepository; +import umc.codeplay.repository.RemixRepository; +import umc.codeplay.repository.TaskRepository; +import umc.codeplay.repository.TrackRepository; + +@Service +@RequiredArgsConstructor +public class ModelService { + + private final TaskRepository taskRepository; + private final HarmonyRepository harmonyRepository; + private final TrackRepository trackRepository; + private final RemixRepository remixRepository; + + public Long setHarmony(ModelRequestDTO.HarmonyRequestDTO harmonyResult) { + Task task = taskRepository.findByIdOrThrow(harmonyResult.getTaskId()); + Harmony harmony = harmonyRepository.findByIdOrThrow(task.getJobId()); + + harmony.updateHarmonyResult( + harmonyResult.getScale(), + harmonyResult.getBpm(), + harmonyResult.getGenre(), + harmonyResult.getVoiceColor()); + return harmonyRepository.save(harmony).getId(); + } + + public Long setTrack(ModelRequestDTO.TrackRequestDTO trackResult) { + Task task = taskRepository.findByIdOrThrow(trackResult.getTaskId()); + Track track = trackRepository.findByIdOrThrow(task.getJobId()); + + track.updateTrackResult( + trackResult.getVocalUrl(), + trackResult.getInstrumentalUrl(), + trackResult.getBassUrl(), + trackResult.getDrumsUrl()); + return trackRepository.save(track).getId(); + } + + public Long setRemix(ModelRequestDTO.RemixRequestDTO remixResult) { + Task task = taskRepository.findByIdOrThrow(remixResult.getTaskId()); + Remix remix = remixRepository.findByIdOrThrow(task.getJobId()); + + remix.setResultMusicUrl(remixResult.getResultMusicUrl()); + return remixRepository.save(remix).getId(); + } +}