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
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,10 @@ awscliv2.zip
# Spring Boot local config
src/main/resources/application-local.yml

# copyPrivateConfig 태스크가 MR_config/local에서 복사해 오는 빌드 산출물 (실제 비밀키 포함 가능)
src/main/resources/application.yml
src/main/resources/application.example.yml

# docs
docs/*
.claude/*
Expand Down
81 changes: 47 additions & 34 deletions MR_config/local/application.example.yml
Original file line number Diff line number Diff line change
@@ -1,52 +1,65 @@
# ================================================================= #
# 🔒 [NOTICE] 본 파일은 로컬 환경 세팅용 '공통 예시 파일'입니다.
# 각자 로컬 PC 사양에 맞게 정보를 입력한 후, 파일 이름을
# 'application.yml'로 변경하여 동일한 경로에 위치시켜 주세요!
# ================================================================= #

spring:
# 1. 로컬 데이터베이스 커넥션 설정 (PostgreSQL 18)
config:
import:
- optional:file:.env[.properties] #.env 파일 자동 로드

application:
name: musereview-backend

# 공통 데이터베이스 접속 규칙 (실제 정보는 각자 .env나 application-prod에서 오버라이딩)
datasource:
driver-class-name: org.postgresql.Driver
url: jdbc:postgresql://localhost:5432/mr_db # [체크] 로컬에 mr_db 빈 데이터베이스를 먼저 생성
username: postgres # [입력] PostgreSQL 계정명
password: "" # [입력] PostgreSQL 비밀번호 (빈값 입력 가능)
url: ${SPRING_DATASOURCE_URL:jdbc:postgresql://localhost:5432/mr_db}
username: ${SPRING_DATASOURCE_USERNAME:postgres}
password: ${SPRING_DATASOURCE_PASSWORD:}

Comment thread
ownue marked this conversation as resolved.
# 2. JPA 및 하이버네이트 구동 설정
# 공통 JPA 및 하이버네이트 구동 설정
jpa:
hibernate:
ddl-auto: update # 엔티티 매핑 정보 변경 시 DB 테이블 자동 반영
show-sql: true # 콘솔에 실행 SQL 포맷 출력
ddl-auto: ${SPRING_JPA_DDL_AUTO:update} # 배포 환경(prod)에선 validate로 덮어씌워짐
show-sql: true
properties:
hibernate:
format_sql: true
dialect: org.hibernate.dialect.PostgreSQLDialect

# 3. Spring Security 및 JWT 인증 설정
# 파일 업로드 용량 제한
servlet:
multipart:
max-file-size: 20MB
max-request-size: 20MB

# Spring Security 및 JWT 인증 설정
jwt:
# [입력] Base64로 인코딩된 256비트(32바이트) 이상의 Secret Key를 채워주세요.
# (주의: Decoders.BASE64.decode를 사용하므로 Plain Text가 아닌 Base64 인코딩 문자열이어야 합니다.)
# 기입 예시: "c29tZS1zZWNyZXQta2V5LW11c3QtYmUtYXQtbGVhc3QtMzItYmF5dGVzLWxvbmc="
secret: ""
secret: ${JWT_SECRET_KEY}
access-token-validity-in-seconds: 1800 # Access Token 만료 시간 (30분)
refresh-token-validity-in-seconds: 604800 # Refresh Token 만료 시간 (7일)

# 4. 외부 소셜 로그인 API 연동 정보 (OAuth용)
# 소셜 로그인 API 연동
oauth:
kakao:
client-id: "" # [입력] 카카오 디벨로퍼스 REST API 키
client-secret: "" # [입력] 카카오 보안 Client Secret 키
redirect-uri: http://localhost:8080/login/oauth2/code/kakao
client-id: ${KAKAO_CLIENT_ID:}
client-secret: ${KAKAO_CLIENT_SECRET:}
redirect-uri: ${KAKAO_REDIRECT_URI:http://localhost:8080/login/oauth2/code/kakao}

google:
client-id: "" # [입력] 구글 클라우드 콘솔 OAuth 클라이언트 ID
client-secret: "" # [입력] 구글 클라우드 콘솔 보안 비밀번호
redirect-uri: http://localhost:8080/login/oauth2/code/google

# 5. AI 서버 연동 설정
ai:
internal:
base-url: http://localhost:8000 # [입력] 로컬에서 띄운 AI 서버 주소
connect-timeout: 3s
read-timeout: 30s
endpoints:
analyze: /analyze
client-id: ${GOOGLE_CLIENT_ID:}
client-secret: ${GOOGLE_CLIENT_SECRET:}
redirect-uri: ${GOOGLE_REDIRECT_URI:http://localhost:8080/login/oauth2/code/google}

# AI 도입 및 AWS S3 공통 구조
external:
ai:
base-url: ${AI_BASE_URL:}
api-key: ${AI_API_KEY:}
model: ${AI_MODEL:}

aws:
s3:
bucket: ${AWS_S3_BUCKET:}
region: ${AWS_REGION:ap-northeast-2}

# 프로필 관련 공통 설정
app:
profile:
default-image-url: ${DEFAULT_PROFILE_IMAGE_URL:}
4 changes: 2 additions & 2 deletions docker-compose.local.yml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
services:
postgres:
image: postgres:16
image: postgres:18
container_name: musereview-postgres
ports:
- "127.0.0.1:5433:5432"
Expand All @@ -9,7 +9,7 @@ services:
POSTGRES_USER: musereview
POSTGRES_PASSWORD: musereview1234
volumes:
- musereview-postgres-data:/var/lib/postgresql/data
- musereview-postgres-data:/var/lib/postgresql

volumes:
musereview-postgres-data:
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
package com.mr.domain.analysis.controller;

import com.mr.domain.analysis.dto.res.AnalysisResultResponseDTO;
import com.mr.domain.analysis.dto.res.AnalysisStatusResponseDTO;
import com.mr.domain.analysis.service.AnalysisService;
import com.mr.global.apipayload.ApiResponse;
import com.mr.global.security.SecurityUtil;
import lombok.RequiredArgsConstructor;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequiredArgsConstructor
@RequestMapping("/api/analyses")
public class AnalysisController {

private final AnalysisService analysisService;

@GetMapping("/{analysisId}/status")
public ApiResponse<AnalysisStatusResponseDTO> getAnalysisStatus(
@PathVariable Long analysisId
) {
Long userId = SecurityUtil.getCurrentUserId();

return ApiResponse.onSuccess(
analysisService.getAnalysisStatus(userId, analysisId)
);
}

@GetMapping("/{analysisId}")
public ApiResponse<AnalysisResultResponseDTO> getAnalysisResult(
@PathVariable Long analysisId
) {
Long userId = SecurityUtil.getCurrentUserId();

return ApiResponse.onSuccess(
analysisService.getAnalysisResult(userId, analysisId)
);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
package com.mr.domain.analysis.dto.res;

import com.fasterxml.jackson.databind.JsonNode;
import com.mr.domain.analysis.entity.Analysis;
import com.mr.domain.analysis.entity.AnalysisReport;
import com.mr.domain.analysis.entity.enums.AnalysisGrade;
import com.mr.domain.analysis.entity.enums.ContentFormat;
import com.mr.domain.analysis.entity.enums.LlmStatus;
import com.mr.domain.analysis.entity.enums.ReportGenerationType;
import java.math.BigDecimal;
import java.time.LocalDateTime;

public record AnalysisResultResponseDTO(
Long analysisId,
Long playingId,
Integer startBar,
Integer endBar,
Integer totalScore,
AnalysisGrade grade,
String summary,
DomainScores domainScores,
Report report,
JsonNode rawResult,
LocalDateTime createdAt,
LocalDateTime completedAt
) {

public static AnalysisResultResponseDTO from(
Analysis analysis,
AnalysisReport analysisReport,
JsonNode rawResult
) {
return new AnalysisResultResponseDTO(
analysis.getId(),
analysis.getPlayingId(),
analysis.getStartBar(),
analysis.getEndBar(),
analysis.getTotalScore(),
analysis.getGrade(),
analysis.getSummary(),
DomainScores.from(analysis),
Report.fromNullable(analysisReport),
rawResult,
analysis.getCreatedAt(),
analysis.getCompletedAt()
);
}

public record DomainScores(
BigDecimal scaleScore,
BigDecimal tensionScore,
BigDecimal progressionScore,
BigDecimal voiceLeadingScore
) {

private static DomainScores from(Analysis analysis) {
return new DomainScores(
analysis.getScaleScore(),
analysis.getTensionScore(),
analysis.getProgressionScore(),
analysis.getVoiceLeadingScore()
);
}
}

public record Report(
Long analysisReportId,
ReportGenerationType generationType,
LlmStatus llmStatus,
ContentFormat contentFormat,
String content,
String modelName,
String promptVersion,
LocalDateTime createdAt,
LocalDateTime updatedAt
) {

private static Report fromNullable(AnalysisReport analysisReport) {
if (analysisReport == null) {
return null;
}

return new Report(
analysisReport.getId(),
analysisReport.getGenerationType(),
analysisReport.getLlmStatus(),
analysisReport.getContentFormat(),
analysisReport.getContent(),
analysisReport.getModelName(),
analysisReport.getPromptVersion(),
analysisReport.getCreatedAt(),
analysisReport.getUpdatedAt()
);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package com.mr.domain.analysis.dto.res;

import com.mr.domain.analysis.entity.Analysis;
import com.mr.domain.analysis.entity.enums.AnalysisStatus;
import java.time.LocalDateTime;

public record AnalysisStatusResponseDTO(
Long analysisId,
AnalysisStatus status,
Integer progressRate,
String message,
LocalDateTime createdAt,
LocalDateTime completedAt
) {

public static AnalysisStatusResponseDTO from(
Analysis analysis,
Integer progressRate,
String message
) {
return new AnalysisStatusResponseDTO(
analysis.getId(),
analysis.getStatus(),
progressRate,
message,
analysis.getCreatedAt(),
analysis.getCompletedAt()
);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,8 @@ public class AnalysisReport extends BaseTimeEntity {
@Column(name = "analysis_report_id")
private Long id;

@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "analysis_id", nullable = false)
@OneToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "analysis_id", nullable = false, unique = true)
Comment thread
coderabbitai[bot] marked this conversation as resolved.
private Analysis analysis;

@Enumerated(EnumType.STRING)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package com.mr.domain.analysis.exception;

import com.mr.global.apipayload.code.BaseCode;
import lombok.AllArgsConstructor;
import lombok.Getter;
import org.springframework.http.HttpStatus;

@Getter
@AllArgsConstructor
public enum AnalysisErrorStatus implements BaseCode {

ANALYSIS_ACCESS_DENIED(HttpStatus.FORBIDDEN, "ANALYSIS_403_01", "해당 분석 결과에 접근할 수 없습니다."),

ANALYSIS_NOT_FOUND(HttpStatus.NOT_FOUND, "ANALYSIS_404_01", "분석 결과를 찾을 수 없습니다."),

ANALYSIS_NOT_COMPLETED(HttpStatus.CONFLICT, "ANALYSIS_409_01", "아직 완료되지 않은 분석입니다."),

INVALID_RAW_RESULT(HttpStatus.INTERNAL_SERVER_ERROR, "ANALYSIS_500_01", "저장된 분석 결과를 처리할 수 없습니다."),

;

private final HttpStatus status;
private final String code;
private final String message;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package com.mr.domain.analysis.repository;

import com.mr.domain.analysis.entity.AnalysisReport;
import com.mr.domain.analysis.entity.enums.LlmStatus;
import java.util.Optional;
import org.springframework.data.jpa.repository.JpaRepository;

public interface AnalysisReportRepository extends JpaRepository<AnalysisReport, Long> {

Optional<AnalysisReport> findFirstByAnalysisIdAndLlmStatusOrderByCreatedAtDesc(
Long analysisId,
LlmStatus llmStatus
);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package com.mr.domain.analysis.repository;

import com.mr.domain.analysis.entity.Analysis;
import org.springframework.data.jpa.repository.JpaRepository;

public interface AnalysisRepository extends JpaRepository<Analysis, Long> {
}
Loading
Loading