Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .github/workflows/build-test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ jobs:
AWS_DEFAULT_REGION: ${{ secrets.AWS_DEFAULT_REGION }}
S3_BUCKET: ${{ secrets.S3_BUCKET }}
JWT_SECRET: ${{ secrets.JWT_SECRET }}
SQS_QUEUE_NAME: ${{ secrets.SQS_QUEUE_NAME }}
# GOOGLE_CLIENT_ID: ${{ secrets.GOOGLE_CLIENT_ID }}
# GOOGLE_CLIENT_SECRET: ${{ secrets.GOOGLE_CLIENT_SECRET }}
# GOOGLE_REDIRECT_URI: ${{ secrets.GOOGLE_REDIRECT_URI }}
Expand Down
3 changes: 3 additions & 0 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,9 @@ dependencies {
// mail
implementation 'org.springframework.boot:spring-boot-starter-mail'
implementation 'org.springframework:spring-context-support'

// sqs
implementation 'io.awspring.cloud:spring-cloud-aws-starter-sqs'
}

tasks.named('test') {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,11 +38,20 @@ public enum ErrorStatus implements BaseErrorCode {

EMAIL_SEND_ERROR(HttpStatus.BAD_REQUEST, "EMAIL400", "메일 발송에 실패하였습니다."),
EMAIL_CODE_ERROR(HttpStatus.BAD_REQUEST, "EMAIL401", "유효한 코드가 아닙니다."),
EMAIL_NOT_VERIFIED(HttpStatus.BAD_REQUEST, "EMAIL402", "인증되지 않은 이메일입니다."),

PASSWORD_CHANGE_FAILED(HttpStatus.BAD_REQUEST, "PASSWORD400", "비밀번호 재설정이 실패하였습니다."),

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", "해당 리믹스 결과를 찾을 수 없습니다.");
REMIX_NOT_FOUND(HttpStatus.BAD_REQUEST, "REMIX400", "해당 리믹스 결과를 찾을 수 없습니다."),
REMIX_NO_CHANGE(HttpStatus.BAD_REQUEST, "REMIX401", "변경 사항이 없습니다."),
JOB_TYPE_NOT_FOUND(HttpStatus.BAD_REQUEST, "JOB400", "해당 작업 타입을 찾을 수 없습니다."),
INVALID_CONFIG(HttpStatus.BAD_REQUEST, "TRACK400", "유효하지 않은 설정입니다."),
INVALID_PARENT_REMIX(HttpStatus.BAD_REQUEST, "REMIX402", "유효하지 않은 부모 리믹스입니다."),

SQS_SEND_ERROR(HttpStatus.INTERNAL_SERVER_ERROR, "SQS500", "SQS 메시지 전송에 실패하였습니다.");

private final HttpStatus httpStatus;
private final String code;
Expand Down
3 changes: 2 additions & 1 deletion src/main/java/umc/codeplay/config/SecurityConfig.java
Original file line number Diff line number Diff line change
Expand Up @@ -60,11 +60,12 @@ public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Excepti
auth
// 로그인, 회원가입 등 토큰 없이 접근해야 하는 API 허용
.requestMatchers(
"/python-model/**",
"/oauth/**",
"/health",
"/health/s3",
"/auth/**",
"/member/**",
"/member/**", // ??
"/v2/api-docs",
"/v3/api-docs",
"/v3/api-docs/**",
Expand Down
27 changes: 27 additions & 0 deletions src/main/java/umc/codeplay/controller/AuthController.java
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import java.util.Collection;
import java.util.stream.Collectors;
import jakarta.mail.MessagingException;
import jakarta.validation.Valid;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;

Expand All @@ -20,6 +21,7 @@
import io.swagger.v3.oas.annotations.tags.Tag;
import umc.codeplay.apiPayLoad.ApiResponse;
import umc.codeplay.apiPayLoad.code.status.ErrorStatus;
import umc.codeplay.apiPayLoad.exception.GeneralException;
import umc.codeplay.apiPayLoad.exception.handler.GeneralHandler;
import umc.codeplay.converter.MemberConverter;
import umc.codeplay.domain.Member;
Expand Down Expand Up @@ -120,6 +122,9 @@ public ApiResponse<MemberResponseDTO.LoginResultDTO> refresh(
}

// 비밀번호 찾기 및 변경. 이메일 인증
@Operation(
summary = "비밀번호 찾기 -> 이메일 요청",
description = "이메일을 통해 인증번호 전송 요청 api, 인증번호는 5분간 유효합니다.")
@PostMapping("/password/reset/request")
public ApiResponse<String> resetPasswordRequest(
@RequestBody MemberRequestDTO.ResetPasswordDTO request) throws MessagingException {
Expand All @@ -128,15 +133,37 @@ public ApiResponse<String> resetPasswordRequest(
}

// 비밀번호 찾기 및 변경. 인증 코드 확인
@Operation(summary = "비밀번호 찾기 -> 이메일 인증하기", description = "인증번호 인증하는 api")
@PostMapping("/password/reset/verify")
public ApiResponse<String> resetPasswordVerify(
@RequestBody MemberRequestDTO.CheckVerificationCodeDTO request) {
boolean isValid = emailService.verifyCode(request.getEmail(), request.getCode());
if (isValid) {
emailService.markVerified(request.getEmail());
return ApiResponse.onSuccess("인증에 성공하였습니다.");
// 이후에 비밀번호 변경 페이지 연결해 주어야 함.
} else {
throw new GeneralHandler(ErrorStatus.EMAIL_CODE_ERROR);
}
}

// 비밀번호 잊었을 때 -> 변경
@Operation(summary = "비밀번호 찾기 -> 재설정", description = "비밀번호 찾기에서 비밀번호 재설정 하는 api")
@PostMapping("/password/reset/change")
public ApiResponse<String> changePassword(
@RequestBody @Valid MemberRequestDTO.ChangePasswordDTO request) {
String email = request.getEmail();

// 인증 확인
if (!emailService.isVerified(email)) {
throw new GeneralException(ErrorStatus.EMAIL_NOT_VERIFIED);
}
boolean isChanged = memberService.newPassword(email, request.getNewPassword());
if (isChanged) {
emailService.invalidateVerificationCode(email);
return ApiResponse.onSuccess("비밀번호 변경이 완료되었습니다.");
} else {
throw new GeneralException(ErrorStatus.PASSWORD_CHANGE_FAILED);
}
}
}
3 changes: 3 additions & 0 deletions src/main/java/umc/codeplay/controller/LikeController.java
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@

import lombok.RequiredArgsConstructor;

import io.swagger.v3.oas.annotations.Operation;
import umc.codeplay.apiPayLoad.ApiResponse;
import umc.codeplay.converter.MusicLikeConverter;
import umc.codeplay.domain.Music;
Expand All @@ -23,6 +24,7 @@ public class LikeController {

private final LikeService likeService;

@Operation(summary = "좋아요 추가")
@PostMapping("/like/add")
public ApiResponse<LikeResponseDTO.addLikeResponseDTO> addLike(
@RequestBody @Valid LikeRequestDTO.addLikeRequestDTO request) {
Expand All @@ -34,6 +36,7 @@ public ApiResponse<LikeResponseDTO.addLikeResponseDTO> addLike(
return ApiResponse.onSuccess(MusicLikeConverter.toLikeResponseDTO(like));
}

@Operation(summary = "좋아요 취소")
@PostMapping("/like/remove")
public ApiResponse<LikeResponseDTO.removeLikeResponseDTO> removeLike(
@RequestBody @Valid LikeRequestDTO.removeLikeRequestDTO request) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,17 +10,20 @@
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.ProcessStatus;
import umc.codeplay.dto.ModelRequestDTO;
import umc.codeplay.dto.ModelResponseDTO;
import umc.codeplay.service.ModelService;
import umc.codeplay.service.TaskService;

@RestController
@RequiredArgsConstructor
@RequestMapping("/python-model")
@Tag(name = "py-model-controller", description = "Python 로컬 서버의 분석 결과를 저장하는 API (프론트엔드 사용 X)")
public class ModelResultController {
public class ModelController {

private final ModelService modelService;
private final TaskService taskService;

@Operation(
summary = "화성 분석 결과 저장",
Expand Down Expand Up @@ -58,4 +61,20 @@ public ApiResponse<ModelResponseDTO.RemixResponseDTO> updateTrack(
.remixId(modelService.setRemix(remixRequestDTO))
.build());
}

@Operation(
summary = "작업 실패 업데이트",
description = "Python 서버의 작업 실패 결과를 저장합니다. 프론트엔드에서 사용하지 않습니다.")
@PostMapping("/fail")
public ApiResponse<ModelResponseDTO.TaskFailDTO> updateTaskFail(
@RequestBody ModelRequestDTO.TaskFailDTO request) {
// TODO: 작업 등록 시 생성되었던 엔티티 제거 로직 추가.
return ApiResponse.onSuccess(
ModelResponseDTO.TaskFailDTO.builder()
.taskId(
taskService
.updateTaskStatus(request.getTaskId(), ProcessStatus.FAILED)
.getId())
.build());
}
}
74 changes: 74 additions & 0 deletions src/main/java/umc/codeplay/controller/TaskController.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
package umc.codeplay.controller;

import org.springframework.validation.annotation.Validated;
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.converter.MemberConverter;
import umc.codeplay.domain.Music;
import umc.codeplay.domain.Task;
import umc.codeplay.dto.MemberRequestDTO;
import umc.codeplay.dto.MemberResponseDTO;
import umc.codeplay.service.ModelService;
import umc.codeplay.service.MusicService;
import umc.codeplay.service.TaskService;

@RestController
@RequestMapping("/task")
@RequiredArgsConstructor
@Validated
@Tag(name = "task-controller", description = "화성분석/스템분리/리믹스 작업 요청 API")
public class TaskController {

private final MusicService musicService;
private final ModelService modelService;
private final TaskService taskService;

@Operation(summary = "화성분석 작업 요청", description = "음악 ID를 받아 화성분석 작업을 요청합니다.")
@PostMapping("/harmony")
public ApiResponse<MemberResponseDTO.TaskProgressDTO> requestHarmonyTask(
@RequestBody @Validated MemberRequestDTO.HarmonyTaskDTO request) {
Music music = musicService.findById(request.getMusicId());

Task task = modelService.sendHarmonyMessage(music);

return ApiResponse.onSuccess(MemberConverter.toTaskProgressDTO(task));
}

@Operation(summary = "스템분리 작업 요청", description = "음악 ID와 스템분리 설정을 받아 스템분리 작업을 요청합니다.")
@PostMapping("/stem")
public ApiResponse<MemberResponseDTO.TaskProgressDTO> requestStemTask(
@RequestBody @Validated MemberRequestDTO.StemTaskDTO request) {
Music music = musicService.findById(request.getMusicId());

Task task = modelService.sendTrackMessage(music, request.getTwoStemConfig());

return ApiResponse.onSuccess(MemberConverter.toTaskProgressDTO(task));
}

@Operation(summary = "리믹스 작업 요청", description = "음악 ID와 리믹스 설정을 받아 리믹스 작업을 요청합니다.")
@PostMapping("/remix")
public ApiResponse<MemberResponseDTO.TaskProgressDTO> requestRemixTask(
@RequestBody @Validated MemberRequestDTO.RemixTaskDTO request) {
Music music = musicService.findById(request.getMusicId());

Task task = modelService.sendRemixMessage(music, request);

return ApiResponse.onSuccess(MemberConverter.toTaskProgressDTO(task));
}

@Operation(summary = "작업 조회", description = "작업 ID를 받아 작업 상태를 조회합니다.")
@PostMapping("/get-task")
public ApiResponse<MemberResponseDTO.TaskProgressDTO> getTask(
@RequestBody @Validated MemberRequestDTO.getTaskDTO request) {
Task task = taskService.findById(request.getTaskId());
return ApiResponse.onSuccess(MemberConverter.toTaskProgressDTO(task));
}
}
10 changes: 10 additions & 0 deletions src/main/java/umc/codeplay/converter/MemberConverter.java
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@

import umc.codeplay.domain.Harmony;
import umc.codeplay.domain.Member;
import umc.codeplay.domain.Task;
import umc.codeplay.domain.Track;
import umc.codeplay.domain.enums.Role;
import umc.codeplay.domain.enums.SocialStatus;
Expand Down Expand Up @@ -81,4 +82,13 @@ public MemberResponseDTO.GetMyTrackDTO toGetMyTrackDTO(Track track, Member membe
member, track.getMusic())) // LikeService에서 좋아요 여부 확인
.build();
}

public static MemberResponseDTO.TaskProgressDTO toTaskProgressDTO(Task task) {
return MemberResponseDTO.TaskProgressDTO.builder()
.taskId(task.getId())
.processStatus(task.getStatus().toString())
.jobType(task.getJobType().toString())
.jobId(task.getJobId())
.build();
}
}
1 change: 1 addition & 0 deletions src/main/java/umc/codeplay/domain/Remix.java
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ public class Remix extends BaseEntity {
@JoinColumn(name = "music_id", nullable = false)
private Music music;

@Setter
@ManyToOne(fetch = FetchType.LAZY)
@Comment("이전 단계 리믹스 ID")
@JoinColumn(name = "parent_remix_id")
Expand Down
1 change: 1 addition & 0 deletions src/main/java/umc/codeplay/domain/Task.java
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ public class Task extends BaseEntity {
// TODO: 추후 BigInteger로 변환
private Long id;

@Setter
@Enumerated(EnumType.STRING)
@Comment("task 진행 상태")
@Builder.Default
Expand Down
1 change: 0 additions & 1 deletion src/main/java/umc/codeplay/domain/enums/ProcessStatus.java
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
public enum ProcessStatus {
// todo: 그냥 예시로 적어둠 변경 필요
REQUESTED,
ONGOING,
COMPLETED,
FAILED;
}
52 changes: 50 additions & 2 deletions src/main/java/umc/codeplay/dto/MemberRequestDTO.java
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
package umc.codeplay.dto;

import jakarta.validation.constraints.Email;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.*;

import lombok.Getter;
import lombok.Setter;
Expand Down Expand Up @@ -42,6 +41,12 @@ public static class CheckVerificationCodeDTO {
String code;
}

@Getter
public static class ChangePasswordDTO {
String email;
String newPassword;
}

@Getter
@Setter
public static class UpdateMemberDTO {
Expand All @@ -59,4 +64,47 @@ public static class SearchByMusicTitleDTO {
@NotBlank(message = "음원 제목은 필수 입력값입니다.")
String musicTitle;
}

@Getter
public static class StemTaskDTO {

@NotNull(message = "음원 id는 필수 입력값입니다.") Long musicId;

@NotBlank(message = "빈칸이라도 아무거나 보내주세요.")
String twoStemConfig;
}

@Getter
public static class HarmonyTaskDTO {

@NotNull(message = "음원 id는 필수 입력값입니다.") private Long musicId;
}

@Getter
public static class RemixTaskDTO {

@NotNull(message = "음원 id는 필수 입력값입니다.") private Long musicId;

private Long parentRemixId;

@Min(value = -12, message = "스케일 변경값은 -12 이상의 값이어야 합니다.")
@Max(value = 12, message = "스케일 변경값은 12 이하의 값이어야 합니다.")
private Integer scaleModulation;

@DecimalMin(value = "0.1", message = "템포 배속값은 0.1 이상의 값이어야 합니다.")
@DecimalMax(value = "4.0", message = "템포 배속값은 4.0 이하의 값이어야 합니다.")
private Double tempoRatio;

@DecimalMin(value = "0.0", message = "리버브 값은 0.0 이상의 값이어야 합니다.")
@DecimalMax(value = "1.0", message = "리버브 값은 1.0 이하의 값이어야 합니다.")
private Double reverbAmount;

private Boolean isChorusOn;
}

@Getter
public static class getTaskDTO {

@NotNull(message = "task id는 필수 입력값입니다.") Long taskId;
}
}
11 changes: 11 additions & 0 deletions src/main/java/umc/codeplay/dto/MemberResponseDTO.java
Original file line number Diff line number Diff line change
Expand Up @@ -80,4 +80,15 @@ public static class GetAllByMusicTitleDTO {
private List<GetMyHarmonyDTO> harmonies;
private List<GetMyTrackDTO> tracks;
}

@Builder
@Getter
@NoArgsConstructor
@AllArgsConstructor
public static class TaskProgressDTO {
Long taskId;
String processStatus;
String jobType;
Long jobId;
}
}
Loading
Loading