Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
17 commits
Select commit Hold shift + click to select a range
dee5145
feat: learning 엔티티 및 enum 구조 추가 (#16)
rkdehdrbs7885-oss Jul 12, 2026
05a564c
feat: 구독(Subscription) 엔티티 및 Enum 추가 (#16)
rkdehdrbs7885-oss Jul 12, 2026
bde4168
feat: 백킹트랙 엔티티 (#20)
rkdehdrbs7885-oss Jul 13, 2026
ee1fb3c
feat: 학습 단계 엔티티 추가, erd 수정 사항 반영(학습, 학습 진행 상태)(#16)
rkdehdrbs7885-oss Jul 13, 2026
ce9e331
feat: 학습 단계 엔티티 구현 (#16)
rkdehdrbs7885-oss Jul 13, 2026
bb8487b
feat: 모범 연주 및 코드별 예시 엔티티 구현 (#16)
rkdehdrbs7885-oss Jul 17, 2026
0c00db6
feat: 임포트 컨벤션 적용 (#16)
rkdehdrbs7885-oss Jul 17, 2026
a558736
refactor: 엔티티 성능 최적화를 위한 인덱스 추가 및 리팩토링 (#16)
rkdehdrbs7885-oss Jul 19, 2026
d2ad998
refactor: 코드 레빗 ai 수정_1(#16)
rkdehdrbs7885-oss Jul 20, 2026
cb0769d
refactor: 코드 래빗 ai 수정_2(#16)
rkdehdrbs7885-oss Jul 20, 2026
218bd51
refactor: 코드 래빗 ai 수정_3(#16)
rkdehdrbs7885-oss Jul 20, 2026
eb93eb7
refactor: 도메인 엔티티 캡슐화 및 매핑 오류 수정 (#16)
rkdehdrbs7885-oss Jul 20, 2026
9839d8e
refactor: 코드 래빗 ai 수정_4(#16)
rkdehdrbs7885-oss Jul 20, 2026
4ee86e5
refctor: 백킹트랙 enum 관련 어노테이션 수정(#16)
rkdehdrbs7885-oss Jul 20, 2026
8cdd426
refactor: 불필요한 enum 삭제(#16)
rkdehdrbs7885-oss Jul 20, 2026
3327c45
refactor: 팀 리뷰 수정_1(#16)
rkdehdrbs7885-oss Jul 21, 2026
36e5199
refactor: 팀 리뷰 수정_2 (#16)
rkdehdrbs7885-oss Jul 21, 2026
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
160 changes: 160 additions & 0 deletions src/main/java/com/mr/domain/backingTrack/entity/BackingTrack.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,160 @@
package com.mr.domain.backingTrack.entity;

import com.mr.domain.backingTrack.entity.enums.AccessLevel;
import com.mr.domain.backingTrack.entity.enums.Level;
import com.mr.domain.backingTrack.entity.enums.ScaleType;
import com.mr.global.entity.BaseTimeDeletedEntity;
import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.EnumType;
import jakarta.persistence.Enumerated;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
import jakarta.persistence.Index;
import jakarta.persistence.JoinColumn;
import jakarta.persistence.Table;
import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;

@Getter
@Entity
@Table(
name = "backing_track",
indexes = {
@Index(name = "idx_bt_genre_play",
columnList = "genre, play_count"),
}
)
@NoArgsConstructor(access = lombok.AccessLevel.PROTECTED)
public class BackingTrack extends BaseTimeDeletedEntity {

@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "backing_track_id")
private Long id;
Comment thread
coderabbitai[bot] marked this conversation as resolved.

// 유저 아이디
@Column(name = "user_id", nullable = false)
private Long userId;
Comment on lines +38 to +40

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

TrackType 확인해 보면 백킹트랙 엔티티에 당장 반영은 되어 있지 않지만, SYSTEM/USER_AUDIO 이렇게 두 개가 존재하는데 SYSTEM에서 생성된 트랙도 user_id를 저장해야 하나요!? 저장하지 않아도 되면 nullable=false를 삭제해야 할 것 같고, 저장해야 한다면 전에 말했던 것처럼 아예 전체 관리자?(삭제된 유저들 데이터 모으는 용) 계정 id를 넣을 생각이셨던 건지 궁금합니다!

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

맞습니다! 지난 회의 때 유저가 계정을 삭제하더라도 데이터를 삭제하지 않고 전체 관리자(또는 탈퇴 처리용) 계정 ID로 변경하여 유지하기로 했습니다!

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

그럼 SYSTEM이 생성한 트랙은 해당 계정 ID로 반영한다는 말씀이신가요?


// 학원 아이디
@Column(name = "academy_id")
private Long academyId;
Comment on lines +38 to +44

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

근데 요거 확인해 보니까 userId는 필수이고 academyId는 선택값이라 학원 트랙도 반드시 특정 사용자 소유로 저장되는 구조로 보입니다!!!

academyId는 트랙의 소유 주체를 나타내는 값인지, 단순히 공개 대상 학원을 나타내는 값인지 궁금합니다. 소유 주체라면 사용자/학원/시스템 트랙의 관계를 구분할 수 있도록 타입과 필수 조건을 함께 정하는 것이 좋을 것 같습니다...!


// 트랙 이름
@Column(name = "title", nullable = false, length = 50)
private String title;

// 장르
@Column(name = "genre", nullable = false, length = 50)
private String genre;

// key
@Column(name = "key_signature", nullable = false, length = 20)
private String keySignature;

// 조성
@Enumerated(EnumType.STRING)
@Column(name = "scale_type", nullable = false)
private ScaleType scaleType;

// 박자
@Column(name = "time_signature", nullable = false, length = 10)
private String timeSignature;

// bpm
@Column(name = "bpm", nullable = false)
private Integer bpm;

// 재생 시간
@Column(name = "playtime_sec", nullable = false)
private Integer playtimeSec;

// 오디오 파일
@Column(name = "audio_file_url", length = 255)
private String audioFileUrl;

@Column(name = "midi_file_url", columnDefinition = "JSON")
private String midiFileUrl;

// 재생 수
@Column(name = "play_count", nullable = false)
private Integer playCount;

// 공개 범위
@Enumerated(EnumType.STRING)
@Column(name = "access_level", nullable = false)
private AccessLevel accessLevel;

// 난이도
@Enumerated(EnumType.STRING)
@Column(name = "level", nullable = false)
private Level level;
Comment thread
ownue marked this conversation as resolved.

@Builder(access = lombok.AccessLevel.PRIVATE)
private BackingTrack(Long userId, Long academyId, String title, String genre,
String keySignature, ScaleType scaleType, String timeSignature,
Integer bpm, Integer playtimeSec, String audioFileUrl,
String midiFileUrl, Integer playCount,
AccessLevel accessLevel, Level level) {
this.userId = userId;
this.academyId = academyId;
this.title = title;
this.genre = genre;
this.keySignature = keySignature;
this.scaleType = scaleType;
this.timeSignature = timeSignature;
this.bpm = bpm;
this.playtimeSec = playtimeSec;
this.audioFileUrl = audioFileUrl;
this.midiFileUrl = midiFileUrl;
this.playCount = playCount != null ? playCount : 0;
this.accessLevel = accessLevel != null ? accessLevel : AccessLevel.PRIVATE;
this.level = level != null ? level : Level.BASIC;
}

public static BackingTrack create(Long userId, Long academyId, String title, String genre,
String keySignature, ScaleType scaleType, String timeSignature,
Integer bpm, Integer playtimeSec, String audioFileUrl,
String midiFileUrl, AccessLevel accessLevel, Level level) {
return BackingTrack.builder()
.userId(userId)
.academyId(academyId)
.title(title)
.genre(genre)
.keySignature(keySignature)
.scaleType(scaleType)
.timeSignature(timeSignature)
.bpm(bpm)
.playtimeSec(playtimeSec)
.audioFileUrl(audioFileUrl)
.midiFileUrl(midiFileUrl)
.playCount(0)
.accessLevel(accessLevel)
.level(level)
.build();
}

public void updateTrackInfo(String title, String genre, String keySignature,
ScaleType scaleType, String timeSignature, Integer bpm,
Integer playtimeSec, AccessLevel accessLevel, Level level) {
this.title = title;
this.genre = genre;
this.keySignature = keySignature;
this.scaleType = scaleType;
this.timeSignature = timeSignature;
this.bpm = bpm;
this.playtimeSec = playtimeSec;
if (accessLevel != null) this.accessLevel = accessLevel;
if (level != null) this.level = level;
}

// 공개 범위 수정
public void changeAccessLevel(AccessLevel accessLevel) {
if (accessLevel != null) {
this.accessLevel = accessLevel;
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
package com.mr.domain.backingTrack.entity;

import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.FetchType;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
import jakarta.persistence.Index;
import jakarta.persistence.JoinColumn;
import jakarta.persistence.ManyToOne;
import jakarta.persistence.Table;
import jakarta.persistence.UniqueConstraint;
import lombok.AccessLevel;
import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;

@Entity
@Getter
@Table(
name = "chord_progression",
uniqueConstraints = {
@UniqueConstraint(
name = "uk_chord_progression_track_measure_seq",
columnNames = {"backing_track_id", "measure_no", "sequence_no"}
)
}
)
@NoArgsConstructor(access = AccessLevel.PROTECTED)
public class ChordProgression{

@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "chord_progression_id")
private Long chordProgressionId;

// 백킹트랙 아이디
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "backing_track_id", nullable = false)
private BackingTrack backingTrack;

// 코드 순서
@Column(name = "sequence_no", nullable = false)
private Integer sequenceNo;

// 마디 번호
@Column(name = "measure_no", nullable = false)
private Integer measureNo;

// 코드명
@Column(name = "chord_name", nullable = false, length = 30)
private String chordName;

@Builder(access = AccessLevel.PRIVATE)
private ChordProgression(BackingTrack backingTrack, Integer sequenceNo, Integer measureNo, String chordName) {
this.backingTrack = backingTrack;
this.sequenceNo = sequenceNo;
this.measureNo = measureNo;
this.chordName = chordName;
}

public static ChordProgression create(BackingTrack backingTrack, Integer sequenceNo, Integer measureNo, String chordName) {
return ChordProgression.builder()
.backingTrack(backingTrack)
.sequenceNo(sequenceNo)
.measureNo(measureNo)
.chordName(chordName)
.build();
}

public void updateChordInfo(Integer sequenceNo, Integer measureNo, String chordName) {
this.sequenceNo = sequenceNo;
this.measureNo = measureNo;
this.chordName = chordName;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package com.mr.domain.backingTrack.entity.enums;

public enum AccessLevel {
PRIVATE,
ACADEMY,
PUBLIC
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package com.mr.domain.backingTrack.entity.enums;

public enum Level {
BASIC,
MED,
ADVANCED
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package com.mr.domain.backingTrack.entity.enums;

public enum ScaleType {
MAJOR,
MINOR
}
86 changes: 86 additions & 0 deletions src/main/java/com/mr/domain/learning/entity/ChordExample.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
package com.mr.domain.learning.entity;

import com.mr.global.entity.BaseCreatedEntity;
import jakarta.persistence.CollectionTable;
import jakarta.persistence.Column;
import jakarta.persistence.ElementCollection;
import jakarta.persistence.Entity;
import jakarta.persistence.FetchType;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
import jakarta.persistence.JoinColumn;
import jakarta.persistence.ManyToOne;
import jakarta.persistence.Table;
import lombok.AccessLevel;
import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;

import java.util.ArrayList;
import java.util.List;

@Entity
@Getter
@Table(
name = "chord_example"
)
@NoArgsConstructor(access = AccessLevel.PROTECTED)
public class ChordExample extends BaseCreatedEntity {

@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "chord_example_id")
private Long id;

// 학습 단계 id
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "learning_step_id", nullable = false)
private LearningStep learningStep;

// 코드명
@Column(name = "chord_name", nullable = false, length = 100)
private String chordName;

// 미디 노트 번호 목록 (chord_example_note 보조 테이블에 매핑)
@ElementCollection(fetch = FetchType.LAZY)
@CollectionTable(
name = "chord_example_note",
joinColumns = @JoinColumn(name = "chord_example_id")
)
@Column(name = "note_number", nullable = false)
private List<Integer> noteNumbers;

// 설명
@Column(name = "description", columnDefinition = "TEXT")
private String description;

@Builder(access = AccessLevel.PRIVATE)
private ChordExample(LearningStep learningStep, String chordName,
List<Integer> noteNumbers, String description) {
this.learningStep = learningStep;
this.chordName = chordName;
this.noteNumbers = noteNumbers != null ? noteNumbers : new ArrayList<>();
this.description = description;
}

public static ChordExample create(LearningStep learningStep, String chordName,
List<Integer> noteNumbers, String description) {
return ChordExample.builder()
.learningStep(learningStep)
.chordName(chordName)
.noteNumbers(noteNumbers)
.description(description)
.build();
}


public void updateChordExample(String chordName, List<Integer> noteNumbers, String description) {
this.chordName = chordName;
if (noteNumbers != null) {
this.noteNumbers.clear();
this.noteNumbers.addAll(noteNumbers);
}
this.description = description;
}
Comment thread
rkdehdrbs7885-oss marked this conversation as resolved.
}
Loading
Loading