From 57beee1d193ca098ab2d014af413b962d3c4bb0d Mon Sep 17 00:00:00 2001 From: elive7 Date: Mon, 1 Dec 2025 11:01:27 +0900 Subject: [PATCH 1/6] =?UTF-8?q?fix=20:=20=EA=B2=80=EC=83=89=20=EA=B2=B0?= =?UTF-8?q?=EA=B3=BC=20=EC=88=9C=EC=84=9C=EB=8C=80=EB=A1=9C=20=EB=B0=98?= =?UTF-8?q?=ED=99=98=ED=95=98=EB=8F=84=EB=A1=9D=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../repository/impl/QueryDslSongQueryRepository.java | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/src/main/java/com/projectlyrics/server/domain/song/repository/impl/QueryDslSongQueryRepository.java b/src/main/java/com/projectlyrics/server/domain/song/repository/impl/QueryDslSongQueryRepository.java index c65b2a36..1e8493ff 100644 --- a/src/main/java/com/projectlyrics/server/domain/song/repository/impl/QueryDslSongQueryRepository.java +++ b/src/main/java/com/projectlyrics/server/domain/song/repository/impl/QueryDslSongQueryRepository.java @@ -14,6 +14,7 @@ import java.util.List; import java.util.Objects; import java.util.Optional; +import java.util.stream.Collectors; import lombok.RequiredArgsConstructor; import org.springframework.data.domain.Pageable; import org.springframework.data.domain.Slice; @@ -138,9 +139,16 @@ public List findAllByIds(List ids) { @Override public List findAllByIdsInListOrder(List songIds) { + String idCsv = songIds.stream() + .map(String::valueOf) + .collect(Collectors.joining(",")); + NumberExpression orderExpression = - Expressions.numberTemplate(Integer.class, "FIELD({0}, {1})", song.id, - songIds.stream().map(String::valueOf).toArray()); + Expressions.numberTemplate( + Integer.class, + "FIELD({0}, " + idCsv + ")", + song.id + ); return jpaQueryFactory .selectFrom(song) From e8f9e835e02d9953b881930ec5f888f869d28328 Mon Sep 17 00:00:00 2001 From: "jin.geonwoo" Date: Sun, 4 Jan 2026 11:23:43 +0900 Subject: [PATCH 2/6] =?UTF-8?q?[SCRUM-261]=20feat:=20Note=EC=97=90=20ENUM?= =?UTF-8?q?=20=ED=83=80=EC=9E=85=20=EC=BB=AC=EB=9F=BC=20'type'=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Note 생성시 type 필드 포함 강제 - Note 조회시 type 필드 추가 --- .../note/dto/request/NoteCreateRequest.java | 3 + .../note/dto/response/NoteDetailResponse.java | 3 + .../note/dto/response/NoteGetResponse.java | 3 + .../server/domain/note/entity/Note.java | 11 +++- .../server/domain/note/entity/NoteCreate.java | 3 + .../server/domain/note/entity/NoteType.java | 31 ++++++++++ .../AuthCommandServiceIntegrationTest.java | 2 + .../service/BookmarkCommandServiceTest.java | 2 + .../service/CommentCommandServiceTest.java | 2 + .../FavoriteArtistQueryServiceTest.java | 3 + .../like/service/LikeCommandServiceTest.java | 2 + .../like/service/LikeQueryServiceTest.java | 2 + .../domain/note/api/NoteControllerTest.java | 2 + .../domain/note/entity/NoteCreateTest.java | 27 +++++++++ .../server/domain/note/entity/NoteTest.java | 6 ++ .../domain/note/entity/NoteTypeTest.java | 59 +++++++++++++++++++ .../note/service/NoteCommandServiceTest.java | 10 ++++ .../note/service/NoteQueryServiceTest.java | 5 ++ .../NotificationCommandServiceTest.java | 2 + .../service/NotificationQueryServiceTest.java | 2 + .../service/ReportCommandServiceTest.java | 2 + .../song/service/SongQueryServiceTest.java | 5 ++ .../view/service/ViewCommandServiceTest.java | 2 + .../server/support/fixture/NoteFixture.java | 10 +++- 24 files changed, 196 insertions(+), 3 deletions(-) create mode 100644 src/main/java/com/projectlyrics/server/domain/note/entity/NoteType.java create mode 100644 src/test/java/com/projectlyrics/server/domain/note/entity/NoteTypeTest.java diff --git a/src/main/java/com/projectlyrics/server/domain/note/dto/request/NoteCreateRequest.java b/src/main/java/com/projectlyrics/server/domain/note/dto/request/NoteCreateRequest.java index 94f8d71f..b7399f5c 100644 --- a/src/main/java/com/projectlyrics/server/domain/note/dto/request/NoteCreateRequest.java +++ b/src/main/java/com/projectlyrics/server/domain/note/dto/request/NoteCreateRequest.java @@ -2,6 +2,7 @@ import com.projectlyrics.server.domain.note.entity.NoteBackground; import com.projectlyrics.server.domain.note.entity.NoteStatus; +import com.projectlyrics.server.domain.note.entity.NoteType; import jakarta.validation.constraints.NotBlank; import jakarta.validation.constraints.NotNull; @@ -12,6 +13,8 @@ public record NoteCreateRequest( NoteBackground background, @NotNull NoteStatus status, + @NotNull(message = "노트 유형이 입력되지 않았습니다.") + NoteType noteType, Long songId ) { } diff --git a/src/main/java/com/projectlyrics/server/domain/note/dto/response/NoteDetailResponse.java b/src/main/java/com/projectlyrics/server/domain/note/dto/response/NoteDetailResponse.java index cc54b13b..07919b93 100644 --- a/src/main/java/com/projectlyrics/server/domain/note/dto/response/NoteDetailResponse.java +++ b/src/main/java/com/projectlyrics/server/domain/note/dto/response/NoteDetailResponse.java @@ -14,6 +14,7 @@ public record NoteDetailResponse( Long id, String content, String status, + String noteType, @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd HH:mm:ss", timezone = "Asia/Seoul") LocalDateTime createdAt, LyricsGetResponse lyrics, @@ -31,6 +32,7 @@ public static NoteDetailResponse of(Note note, List comments, Long user note.getId(), note.getContent(), note.getNoteStatus().name(), + note.getNoteType().name(), note.getCreatedAt(), LyricsGetResponse.from(note.getLyrics()), UserGetResponse.from(note.getPublisher()), @@ -50,6 +52,7 @@ public static NoteDetailResponse of(Note note, List comments, Long user note.getId(), note.getContent(), note.getNoteStatus().name(), + note.getNoteType().name(), createdAt, LyricsGetResponse.from(note.getLyrics()), UserGetResponse.from(note.getPublisher()), diff --git a/src/main/java/com/projectlyrics/server/domain/note/dto/response/NoteGetResponse.java b/src/main/java/com/projectlyrics/server/domain/note/dto/response/NoteGetResponse.java index 7d7777a6..9d643e61 100644 --- a/src/main/java/com/projectlyrics/server/domain/note/dto/response/NoteGetResponse.java +++ b/src/main/java/com/projectlyrics/server/domain/note/dto/response/NoteGetResponse.java @@ -12,6 +12,7 @@ public record NoteGetResponse( Long id, String content, String status, + String noteType, @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd HH:mm:ss", timezone = "Asia/Seoul") LocalDateTime createdAt, LyricsGetResponse lyrics, @@ -28,6 +29,7 @@ public static NoteGetResponse of(Note note, Long userId) { note.getId(), note.getContent(), note.getNoteStatus().name(), + note.getNoteType().name(), note.getCreatedAt(), LyricsGetResponse.from(note.getLyrics()), UserGetResponse.from(note.getPublisher()), @@ -44,6 +46,7 @@ public static NoteGetResponse of(Note note, Long userId, LocalDateTime createdAt note.getId(), note.getContent(), note.getNoteStatus().name(), + note.getNoteType().name(), createdAt, LyricsGetResponse.from(note.getLyrics()), UserGetResponse.from(note.getPublisher()), diff --git a/src/main/java/com/projectlyrics/server/domain/note/entity/Note.java b/src/main/java/com/projectlyrics/server/domain/note/entity/Note.java index 89d7cff8..355735ea 100644 --- a/src/main/java/com/projectlyrics/server/domain/note/entity/Note.java +++ b/src/main/java/com/projectlyrics/server/domain/note/entity/Note.java @@ -34,6 +34,10 @@ public class Note extends BaseEntity { @Enumerated(EnumType.STRING) private NoteStatus noteStatus; + @Enumerated(EnumType.STRING) + @Column(nullable = false, columnDefinition = "VARCHAR(50) DEFAULT 'FREE'") + private NoteType noteType; + @OneToOne(fetch = FetchType.LAZY, cascade = CascadeType.ALL, orphanRemoval = true) @JoinColumn(name="lyrics_id") private Lyrics lyrics; @@ -58,12 +62,14 @@ private Note( String content, Lyrics lyrics, NoteStatus noteStatus, + NoteType noteType, User publisher, Song song ) { this.id = id; this.content = content; this.noteStatus = noteStatus; + this.noteType = noteType; this.publisher = publisher; this.song = song; addLyrics(lyrics); @@ -73,10 +79,11 @@ private Note( String content, Lyrics lyrics, NoteStatus noteStatus, + NoteType noteType, User publisher, Song song ) { - this(null, content, lyrics, noteStatus, publisher, song); + this(null, content, lyrics, noteStatus, noteType, publisher, song); } public static Note create(NoteCreate noteCreate) { @@ -84,6 +91,7 @@ public static Note create(NoteCreate noteCreate) { noteCreate.content(), Lyrics.of(noteCreate.lyrics(), noteCreate.background()), noteCreate.status(), + noteCreate.noteType(), noteCreate.publisher(), noteCreate.song() ); @@ -95,6 +103,7 @@ public static Note createWithId(Long id, NoteCreate noteCreate) { noteCreate.content(), Lyrics.of(noteCreate.lyrics(), noteCreate.background()), noteCreate.status(), + noteCreate.noteType(), noteCreate.publisher(), noteCreate.song() ); diff --git a/src/main/java/com/projectlyrics/server/domain/note/entity/NoteCreate.java b/src/main/java/com/projectlyrics/server/domain/note/entity/NoteCreate.java index a293112c..5a55bd1d 100644 --- a/src/main/java/com/projectlyrics/server/domain/note/entity/NoteCreate.java +++ b/src/main/java/com/projectlyrics/server/domain/note/entity/NoteCreate.java @@ -11,12 +11,14 @@ public record NoteCreate( String lyrics, NoteBackground background, NoteStatus status, + NoteType noteType, User publisher, Song song ) { public static NoteCreate from(NoteCreateRequest request, User publisher, Song song) { checkNull(request.status()); + checkNull(request.noteType()); checkNull(publisher); checkNull(song); @@ -25,6 +27,7 @@ public static NoteCreate from(NoteCreateRequest request, User publisher, Song so request.lyrics(), request.background(), request.status(), + request.noteType(), publisher, song ); diff --git a/src/main/java/com/projectlyrics/server/domain/note/entity/NoteType.java b/src/main/java/com/projectlyrics/server/domain/note/entity/NoteType.java new file mode 100644 index 00000000..834c6d02 --- /dev/null +++ b/src/main/java/com/projectlyrics/server/domain/note/entity/NoteType.java @@ -0,0 +1,31 @@ +package com.projectlyrics.server.domain.note.entity; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonValue; +import lombok.RequiredArgsConstructor; + +import java.util.Arrays; + +@RequiredArgsConstructor +public enum NoteType { + + FREE("FREE"), + QUESTION("QUESTION"), + LYRICS_ANALYSIS("LYRICS_ANALYSIS"), + ; + + private final String type; + + @JsonValue + public String getType() { + return type; + } + + @JsonCreator + public static NoteType of(String type) { + return Arrays.stream(NoteType.values()) + .filter(noteType -> noteType.type.equals(type)) + .findFirst() + .orElseThrow(() -> new IllegalArgumentException("Invalid NoteType: " + type)); + } +} diff --git a/src/test/java/com/projectlyrics/server/domain/auth/service/AuthCommandServiceIntegrationTest.java b/src/test/java/com/projectlyrics/server/domain/auth/service/AuthCommandServiceIntegrationTest.java index d11322e9..6f67882f 100644 --- a/src/test/java/com/projectlyrics/server/domain/auth/service/AuthCommandServiceIntegrationTest.java +++ b/src/test/java/com/projectlyrics/server/domain/auth/service/AuthCommandServiceIntegrationTest.java @@ -25,6 +25,7 @@ import com.projectlyrics.server.domain.note.dto.request.NoteCreateRequest; import com.projectlyrics.server.domain.note.entity.Note; import com.projectlyrics.server.domain.note.entity.NoteStatus; +import com.projectlyrics.server.domain.note.entity.NoteType; import com.projectlyrics.server.domain.note.repository.NoteQueryRepository; import com.projectlyrics.server.domain.note.service.NoteCommandService; import com.projectlyrics.server.domain.song.entity.Song; @@ -339,6 +340,7 @@ private Note writeNote(Long userId) { null, null, NoteStatus.PUBLISHED, + NoteType.FREE, song.getId() ); return noteCommandService.create(noteCreateRequest, userId); diff --git a/src/test/java/com/projectlyrics/server/domain/bookmark/service/BookmarkCommandServiceTest.java b/src/test/java/com/projectlyrics/server/domain/bookmark/service/BookmarkCommandServiceTest.java index f4acca59..5220c23e 100644 --- a/src/test/java/com/projectlyrics/server/domain/bookmark/service/BookmarkCommandServiceTest.java +++ b/src/test/java/com/projectlyrics/server/domain/bookmark/service/BookmarkCommandServiceTest.java @@ -13,6 +13,7 @@ import com.projectlyrics.server.domain.note.entity.NoteBackground; import com.projectlyrics.server.domain.note.entity.NoteCreate; import com.projectlyrics.server.domain.note.entity.NoteStatus; +import com.projectlyrics.server.domain.note.entity.NoteType; import com.projectlyrics.server.domain.note.repository.NoteCommandRepository; import com.projectlyrics.server.domain.note.repository.NoteQueryRepository; import com.projectlyrics.server.domain.song.entity.Song; @@ -75,6 +76,7 @@ void setUp() { "lyrics", NoteBackground.DEFAULT, NoteStatus.PUBLISHED, + NoteType.FREE, song.getId() ); note = noteCommandRepository.save(Note.create(NoteCreate.from(noteCreateRequest, user, song))); diff --git a/src/test/java/com/projectlyrics/server/domain/comment/service/CommentCommandServiceTest.java b/src/test/java/com/projectlyrics/server/domain/comment/service/CommentCommandServiceTest.java index e7d72095..b3b3c275 100644 --- a/src/test/java/com/projectlyrics/server/domain/comment/service/CommentCommandServiceTest.java +++ b/src/test/java/com/projectlyrics/server/domain/comment/service/CommentCommandServiceTest.java @@ -19,6 +19,7 @@ import com.projectlyrics.server.domain.note.entity.NoteBackground; import com.projectlyrics.server.domain.note.entity.NoteCreate; import com.projectlyrics.server.domain.note.entity.NoteStatus; +import com.projectlyrics.server.domain.note.entity.NoteType; import com.projectlyrics.server.domain.note.repository.NoteCommandRepository; import com.projectlyrics.server.domain.note.repository.NoteQueryRepository; import com.projectlyrics.server.domain.song.entity.Song; @@ -76,6 +77,7 @@ void setUp() { "lyrics", NoteBackground.DEFAULT, NoteStatus.PUBLISHED, + NoteType.FREE, song.getId() ); note = noteCommandRepository.save(Note.create(NoteCreate.from(noteCreateRequest, user, song))); diff --git a/src/test/java/com/projectlyrics/server/domain/favoriteartist/service/FavoriteArtistQueryServiceTest.java b/src/test/java/com/projectlyrics/server/domain/favoriteartist/service/FavoriteArtistQueryServiceTest.java index cc30ad47..e89a8c0a 100644 --- a/src/test/java/com/projectlyrics/server/domain/favoriteartist/service/FavoriteArtistQueryServiceTest.java +++ b/src/test/java/com/projectlyrics/server/domain/favoriteartist/service/FavoriteArtistQueryServiceTest.java @@ -11,6 +11,7 @@ import com.projectlyrics.server.domain.note.entity.Note; import com.projectlyrics.server.domain.note.entity.NoteBackground; import com.projectlyrics.server.domain.note.entity.NoteStatus; +import com.projectlyrics.server.domain.note.entity.NoteType; import com.projectlyrics.server.domain.note.service.NoteCommandService; import com.projectlyrics.server.domain.song.entity.Song; import com.projectlyrics.server.domain.song.repository.SongCommandRepository; @@ -74,6 +75,7 @@ void setUp() { "lyrics", NoteBackground.DEFAULT, NoteStatus.PUBLISHED, + NoteType.FREE, song1.getId() ); @@ -82,6 +84,7 @@ void setUp() { "lyrics", NoteBackground.DEFAULT, NoteStatus.PUBLISHED, + NoteType.FREE, song2.getId() ); } diff --git a/src/test/java/com/projectlyrics/server/domain/like/service/LikeCommandServiceTest.java b/src/test/java/com/projectlyrics/server/domain/like/service/LikeCommandServiceTest.java index 7e60f6d6..303bc916 100644 --- a/src/test/java/com/projectlyrics/server/domain/like/service/LikeCommandServiceTest.java +++ b/src/test/java/com/projectlyrics/server/domain/like/service/LikeCommandServiceTest.java @@ -12,6 +12,7 @@ import com.projectlyrics.server.domain.note.entity.NoteBackground; import com.projectlyrics.server.domain.note.entity.NoteCreate; import com.projectlyrics.server.domain.note.entity.NoteStatus; +import com.projectlyrics.server.domain.note.entity.NoteType; import com.projectlyrics.server.domain.note.repository.NoteCommandRepository; import com.projectlyrics.server.domain.note.repository.NoteQueryRepository; import com.projectlyrics.server.domain.song.entity.Song; @@ -73,6 +74,7 @@ void setUp() { "lyrics", NoteBackground.DEFAULT, NoteStatus.PUBLISHED, + NoteType.FREE, song.getId() ); note = noteCommandRepository.save(Note.create(NoteCreate.from(noteCreateRequest, user, song))); diff --git a/src/test/java/com/projectlyrics/server/domain/like/service/LikeQueryServiceTest.java b/src/test/java/com/projectlyrics/server/domain/like/service/LikeQueryServiceTest.java index 0ccd3ef2..6f01706c 100644 --- a/src/test/java/com/projectlyrics/server/domain/like/service/LikeQueryServiceTest.java +++ b/src/test/java/com/projectlyrics/server/domain/like/service/LikeQueryServiceTest.java @@ -8,6 +8,7 @@ import com.projectlyrics.server.domain.note.entity.NoteBackground; import com.projectlyrics.server.domain.note.entity.NoteCreate; import com.projectlyrics.server.domain.note.entity.NoteStatus; +import com.projectlyrics.server.domain.note.entity.NoteType; import com.projectlyrics.server.domain.note.repository.NoteCommandRepository; import com.projectlyrics.server.domain.note.repository.NoteQueryRepository; import com.projectlyrics.server.domain.song.entity.Song; @@ -67,6 +68,7 @@ void setUp() { "lyrics", NoteBackground.DEFAULT, NoteStatus.PUBLISHED, + NoteType.FREE, song.getId() ); note = noteCommandRepository.save(Note.create(NoteCreate.from(noteCreateRequest, user1, song))); diff --git a/src/test/java/com/projectlyrics/server/domain/note/api/NoteControllerTest.java b/src/test/java/com/projectlyrics/server/domain/note/api/NoteControllerTest.java index 70824513..7030ecb0 100644 --- a/src/test/java/com/projectlyrics/server/domain/note/api/NoteControllerTest.java +++ b/src/test/java/com/projectlyrics/server/domain/note/api/NoteControllerTest.java @@ -25,6 +25,7 @@ import com.projectlyrics.server.domain.note.entity.Note; import com.projectlyrics.server.domain.note.entity.NoteBackground; import com.projectlyrics.server.domain.note.entity.NoteStatus; +import com.projectlyrics.server.domain.note.entity.NoteType; import com.projectlyrics.server.domain.user.entity.ProfileCharacter; import com.projectlyrics.server.domain.user.entity.User; import com.projectlyrics.server.support.RestDocsTest; @@ -55,6 +56,7 @@ class NoteControllerTest extends RestDocsTest { "나의 꽃이 피는 날이 올 수 있을까", NoteBackground.DEFAULT, NoteStatus.PUBLISHED, + NoteType.FREE, 1L ); diff --git a/src/test/java/com/projectlyrics/server/domain/note/entity/NoteCreateTest.java b/src/test/java/com/projectlyrics/server/domain/note/entity/NoteCreateTest.java index f555bb01..e51b8c10 100644 --- a/src/test/java/com/projectlyrics/server/domain/note/entity/NoteCreateTest.java +++ b/src/test/java/com/projectlyrics/server/domain/note/entity/NoteCreateTest.java @@ -27,6 +27,7 @@ class NoteCreateTest { "lyrics", NoteBackground.DEFAULT, NoteStatus.PUBLISHED, + NoteType.FREE, song.getId() ); @@ -39,6 +40,7 @@ class NoteCreateTest { () -> assertThat(result.lyrics()).isEqualTo(request.lyrics()), () -> assertThat(result.background()).isEqualTo(request.background()), () -> assertThat(result.status()).isEqualTo(request.status()), + () -> assertThat(result.noteType()).isEqualTo(request.noteType()), () -> assertThat(result.publisher()).isEqualTo(publisher), () -> assertThat(result.song()).isEqualTo(song) ); @@ -55,6 +57,7 @@ class NoteCreateTest { null, NoteBackground.DEFAULT, NoteStatus.PUBLISHED, + NoteType.FREE, song.getId() ); @@ -67,6 +70,7 @@ class NoteCreateTest { () -> assertThat(result.lyrics()).isEqualTo(request.lyrics()), () -> assertThat(result.background()).isEqualTo(request.background()), () -> assertThat(result.status()).isEqualTo(request.status()), + () -> assertThat(result.noteType()).isEqualTo(request.noteType()), () -> assertThat(result.publisher()).isEqualTo(publisher), () -> assertThat(result.song()).isEqualTo(song) ); @@ -83,6 +87,7 @@ class NoteCreateTest { "lyrics", NoteBackground.DEFAULT, null, + NoteType.FREE, song.getId() ); @@ -102,6 +107,7 @@ class NoteCreateTest { "lyrics", NoteBackground.DEFAULT, NoteStatus.PUBLISHED, + NoteType.FREE, song.getId() ); @@ -121,6 +127,7 @@ class NoteCreateTest { "lyrics", NoteBackground.DEFAULT, NoteStatus.PUBLISHED, + NoteType.FREE, 1L ); @@ -128,4 +135,24 @@ class NoteCreateTest { assertThatThrownBy(() -> NoteCreate.from(request, publisher, song)) .isInstanceOf(DomainNullFieldException.class); } + + @Test + void 널_노트_유형에_대해_예외를_발생시켜야_한다() { + // given + Artist artist = ArtistFixture.create(); + User publisher = UserFixture.create(); + Song song = SongFixture.create(artist); + NoteCreateRequest request = new NoteCreateRequest( + "content", + "lyrics", + NoteBackground.DEFAULT, + NoteStatus.PUBLISHED, + null, + song.getId() + ); + + // when & then + assertThatThrownBy(() -> NoteCreate.from(request, publisher, song)) + .isInstanceOf(DomainNullFieldException.class); + } } \ No newline at end of file diff --git a/src/test/java/com/projectlyrics/server/domain/note/entity/NoteTest.java b/src/test/java/com/projectlyrics/server/domain/note/entity/NoteTest.java index bd1e7102..b6b1dd1a 100644 --- a/src/test/java/com/projectlyrics/server/domain/note/entity/NoteTest.java +++ b/src/test/java/com/projectlyrics/server/domain/note/entity/NoteTest.java @@ -22,6 +22,7 @@ class NoteTest { null, null, NoteStatus.PUBLISHED, + NoteType.FREE, publisher, song ); @@ -34,6 +35,7 @@ class NoteTest { () -> assertThat(note.getContent()).isEqualTo("content"), () -> assertThat(note.getLyrics()).isNull(), () -> assertThat(note.getNoteStatus()).isEqualTo(NoteStatus.PUBLISHED), + () -> assertThat(note.getNoteType()).isEqualTo(NoteType.FREE), () -> assertThat(note.getPublisher()).isEqualTo(publisher), () -> assertThat(note.getSong()).isEqualTo(song) ); @@ -51,6 +53,7 @@ class NoteTest { null, null, NoteStatus.PUBLISHED, + NoteType.FREE, publisher, song ); @@ -73,6 +76,7 @@ class NoteTest { null, null, NoteStatus.DRAFT, + NoteType.FREE, publisher, song ); @@ -108,6 +112,7 @@ class NoteTest { null, null, NoteStatus.PUBLISHED, + NoteType.FREE, publisher, song ); @@ -137,6 +142,7 @@ class NoteTest { "lyrics", NoteBackground.DEFAULT, NoteStatus.PUBLISHED, + NoteType.FREE, publisher, song ); diff --git a/src/test/java/com/projectlyrics/server/domain/note/entity/NoteTypeTest.java b/src/test/java/com/projectlyrics/server/domain/note/entity/NoteTypeTest.java new file mode 100644 index 00000000..8c2c53a6 --- /dev/null +++ b/src/test/java/com/projectlyrics/server/domain/note/entity/NoteTypeTest.java @@ -0,0 +1,59 @@ +package com.projectlyrics.server.domain.note.entity; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ValueSource; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +class NoteTypeTest { + + @Test + void FREE_타입을_문자열로부터_생성해야_한다() { + // when + NoteType result = NoteType.of("FREE"); + + // then + assertThat(result).isEqualTo(NoteType.FREE); + } + + @Test + void QUESTION_타입을_문자열로부터_생성해야_한다() { + // when + NoteType result = NoteType.of("QUESTION"); + + // then + assertThat(result).isEqualTo(NoteType.QUESTION); + } + + @Test + void LYRICS_ANALYSIS_타입을_문자열로부터_생성해야_한다() { + // when + NoteType result = NoteType.of("LYRICS_ANALYSIS"); + + // then + assertThat(result).isEqualTo(NoteType.LYRICS_ANALYSIS); + } + + @ParameterizedTest + @ValueSource(strings = {"INVALID", "free", "question", "", " "}) + void 유효하지_않은_문자열에_대해_예외를_발생시켜야_한다(String invalidType) { + // when & then + assertThatThrownBy(() -> NoteType.of(invalidType)) + .isInstanceOf(IllegalArgumentException.class) + .hasMessageContaining("Invalid NoteType"); + } + + @Test + void getType_메서드는_타입_문자열을_반환해야_한다() { + // given + NoteType noteType = NoteType.LYRICS_ANALYSIS; + + // when + String result = noteType.getType(); + + // then + assertThat(result).isEqualTo("LYRICS_ANALYSIS"); + } +} diff --git a/src/test/java/com/projectlyrics/server/domain/note/service/NoteCommandServiceTest.java b/src/test/java/com/projectlyrics/server/domain/note/service/NoteCommandServiceTest.java index 79919d3b..80b0e462 100644 --- a/src/test/java/com/projectlyrics/server/domain/note/service/NoteCommandServiceTest.java +++ b/src/test/java/com/projectlyrics/server/domain/note/service/NoteCommandServiceTest.java @@ -13,6 +13,7 @@ import com.projectlyrics.server.domain.note.entity.Note; import com.projectlyrics.server.domain.note.entity.NoteBackground; import com.projectlyrics.server.domain.note.entity.NoteStatus; +import com.projectlyrics.server.domain.note.entity.NoteType; import com.projectlyrics.server.domain.note.exception.InvalidNoteDeletionException; import com.projectlyrics.server.domain.note.exception.InvalidNoteUpdateException; import com.projectlyrics.server.domain.note.exception.TooManyDraftsException; @@ -63,6 +64,7 @@ class NoteCommandServiceTest extends IntegrationTest { "lyrics", NoteBackground.DEFAULT, NoteStatus.PUBLISHED, + NoteType.FREE, song.getId() ); @@ -94,6 +96,7 @@ class NoteCommandServiceTest extends IntegrationTest { "lyrics", NoteBackground.DEFAULT, NoteStatus.DRAFT, + NoteType.FREE, song.getId() ); @@ -118,6 +121,7 @@ class NoteCommandServiceTest extends IntegrationTest { "lyrics", NoteBackground.DEFAULT, NoteStatus.PUBLISHED, + NoteType.FREE, song.getId() ); Note note = sut.create(request, user.getId()); @@ -148,6 +152,7 @@ class NoteCommandServiceTest extends IntegrationTest { "lyrics", NoteBackground.DEFAULT, NoteStatus.PUBLISHED, + NoteType.FREE, song.getId() ); Note note = sut.create(request, publisher.getId()); @@ -170,6 +175,7 @@ class NoteCommandServiceTest extends IntegrationTest { "lyrics", NoteBackground.DEFAULT, NoteStatus.DRAFT, + NoteType.FREE, song.getId() ); Note note = sut.create(createRequest, user.getId()); @@ -208,6 +214,7 @@ class NoteCommandServiceTest extends IntegrationTest { null, null, NoteStatus.DRAFT, + NoteType.FREE, song.getId() ); Note note = sut.create(createRequest, user.getId()); @@ -243,6 +250,7 @@ class NoteCommandServiceTest extends IntegrationTest { null, null, NoteStatus.DRAFT, + NoteType.FREE, song.getId() ); Note note = sut.create(createRequest, publisher.getId()); @@ -270,6 +278,7 @@ class NoteCommandServiceTest extends IntegrationTest { "lyrics", NoteBackground.DEFAULT, NoteStatus.PUBLISHED, + NoteType.FREE, song.getId() ); disciplineCommandRepository.save(DisciplineFixture.createForAll(artist, user)); @@ -290,6 +299,7 @@ class NoteCommandServiceTest extends IntegrationTest { "lyrics", NoteBackground.DEFAULT, NoteStatus.PUBLISHED, + NoteType.FREE, song.getId() ); disciplineCommandRepository.save(DisciplineFixture.createForAll(artist, user)); diff --git a/src/test/java/com/projectlyrics/server/domain/note/service/NoteQueryServiceTest.java b/src/test/java/com/projectlyrics/server/domain/note/service/NoteQueryServiceTest.java index 55d4988c..ef2b9da0 100644 --- a/src/test/java/com/projectlyrics/server/domain/note/service/NoteQueryServiceTest.java +++ b/src/test/java/com/projectlyrics/server/domain/note/service/NoteQueryServiceTest.java @@ -17,6 +17,7 @@ import com.projectlyrics.server.domain.note.entity.Note; import com.projectlyrics.server.domain.note.entity.NoteBackground; import com.projectlyrics.server.domain.note.entity.NoteStatus; +import com.projectlyrics.server.domain.note.entity.NoteType; import com.projectlyrics.server.domain.song.entity.Song; import com.projectlyrics.server.domain.song.repository.SongCommandRepository; import com.projectlyrics.server.domain.user.entity.User; @@ -88,6 +89,7 @@ void setUp() { "lyrics", NoteBackground.DEFAULT, NoteStatus.PUBLISHED, + NoteType.FREE, unlikedArtistSong.getId() ); likedArtistSongNoteRequest = new NoteCreateRequest( @@ -95,6 +97,7 @@ void setUp() { "lyrics", NoteBackground.DEFAULT, NoteStatus.PUBLISHED, + NoteType.FREE, likedArtistSong.getId() ); likedArtistSongNoteRequestWithoutLyrics = new NoteCreateRequest( @@ -102,6 +105,7 @@ void setUp() { null, null, NoteStatus.PUBLISHED, + NoteType.FREE, likedArtistSong.getId() ); } @@ -290,6 +294,7 @@ void setUp() { null, null, NoteStatus.PUBLISHED, + NoteType.FREE, likedArtistSong.getId() ); diff --git a/src/test/java/com/projectlyrics/server/domain/notification/service/NotificationCommandServiceTest.java b/src/test/java/com/projectlyrics/server/domain/notification/service/NotificationCommandServiceTest.java index b25ceae2..b30ffc7b 100644 --- a/src/test/java/com/projectlyrics/server/domain/notification/service/NotificationCommandServiceTest.java +++ b/src/test/java/com/projectlyrics/server/domain/notification/service/NotificationCommandServiceTest.java @@ -25,6 +25,7 @@ import com.projectlyrics.server.domain.note.entity.NoteBackground; import com.projectlyrics.server.domain.note.entity.NoteCreate; import com.projectlyrics.server.domain.note.entity.NoteStatus; +import com.projectlyrics.server.domain.note.entity.NoteType; import com.projectlyrics.server.domain.note.repository.NoteCommandRepository; import com.projectlyrics.server.domain.notification.api.dto.response.NotificationGetResponse; import com.projectlyrics.server.domain.notification.domain.Notification; @@ -109,6 +110,7 @@ void setUp() { "lyrics", NoteBackground.DEFAULT, NoteStatus.PUBLISHED, + NoteType.FREE, song.getId() ); Note note = noteCommandRepository.save(Note.create(NoteCreate.from(noteCreateRequest, user, song))); diff --git a/src/test/java/com/projectlyrics/server/domain/notification/service/NotificationQueryServiceTest.java b/src/test/java/com/projectlyrics/server/domain/notification/service/NotificationQueryServiceTest.java index ce37fb0e..a5de73cd 100644 --- a/src/test/java/com/projectlyrics/server/domain/notification/service/NotificationQueryServiceTest.java +++ b/src/test/java/com/projectlyrics/server/domain/notification/service/NotificationQueryServiceTest.java @@ -11,6 +11,7 @@ import com.projectlyrics.server.domain.note.entity.NoteBackground; import com.projectlyrics.server.domain.note.entity.NoteCreate; import com.projectlyrics.server.domain.note.entity.NoteStatus; +import com.projectlyrics.server.domain.note.entity.NoteType; import com.projectlyrics.server.domain.note.repository.NoteCommandRepository; import com.projectlyrics.server.domain.notification.api.dto.response.NotificationGetResponse; import com.projectlyrics.server.domain.notification.repository.NotificationCommandRepository; @@ -81,6 +82,7 @@ void setUp() { "lyrics", NoteBackground.DEFAULT, NoteStatus.PUBLISHED, + NoteType.FREE, song.getId() ); note = noteCommandRepository.save(Note.create(NoteCreate.from(noteCreateRequest, user, song))); diff --git a/src/test/java/com/projectlyrics/server/domain/report/service/ReportCommandServiceTest.java b/src/test/java/com/projectlyrics/server/domain/report/service/ReportCommandServiceTest.java index 00fd57fc..2079c8a2 100644 --- a/src/test/java/com/projectlyrics/server/domain/report/service/ReportCommandServiceTest.java +++ b/src/test/java/com/projectlyrics/server/domain/report/service/ReportCommandServiceTest.java @@ -16,6 +16,7 @@ import com.projectlyrics.server.domain.note.entity.NoteBackground; import com.projectlyrics.server.domain.note.entity.NoteCreate; import com.projectlyrics.server.domain.note.entity.NoteStatus; +import com.projectlyrics.server.domain.note.entity.NoteType; import com.projectlyrics.server.domain.note.repository.NoteCommandRepository; import com.projectlyrics.server.domain.report.domain.ApprovalStatus; import com.projectlyrics.server.domain.report.domain.Report; @@ -87,6 +88,7 @@ void setUp() { "lyrics", NoteBackground.DEFAULT, NoteStatus.PUBLISHED, + NoteType.FREE, song.getId() ); note = noteCommandRepository.save(Note.create(NoteCreate.from(noteCreateRequest, user, song))); diff --git a/src/test/java/com/projectlyrics/server/domain/song/service/SongQueryServiceTest.java b/src/test/java/com/projectlyrics/server/domain/song/service/SongQueryServiceTest.java index 58124675..de727e42 100644 --- a/src/test/java/com/projectlyrics/server/domain/song/service/SongQueryServiceTest.java +++ b/src/test/java/com/projectlyrics/server/domain/song/service/SongQueryServiceTest.java @@ -15,6 +15,7 @@ import com.projectlyrics.server.domain.note.entity.NoteBackground; import com.projectlyrics.server.domain.note.entity.NoteCreate; import com.projectlyrics.server.domain.note.entity.NoteStatus; +import com.projectlyrics.server.domain.note.entity.NoteType; import com.projectlyrics.server.domain.note.repository.NoteCommandRepository; import com.projectlyrics.server.domain.search.domain.SongSearch; import com.projectlyrics.server.domain.search.repository.SearchRepository; @@ -145,6 +146,7 @@ void setUp() { "lyrics", NoteBackground.DEFAULT, NoteStatus.PUBLISHED, + NoteType.FREE, song1.getId() ); NoteCreateRequest requestOfSong2 = new NoteCreateRequest( @@ -152,6 +154,7 @@ void setUp() { "lyrics", NoteBackground.DEFAULT, NoteStatus.PUBLISHED, + NoteType.FREE, song2.getId() ); NoteCreateRequest requestOfSong3 = new NoteCreateRequest( @@ -159,6 +162,7 @@ void setUp() { "lyrics", NoteBackground.DEFAULT, NoteStatus.PUBLISHED, + NoteType.FREE, song3.getId() ); @@ -241,6 +245,7 @@ void setUp() { "lyrics", NoteBackground.DEFAULT, NoteStatus.PUBLISHED, + NoteType.FREE, song.getId() ); diff --git a/src/test/java/com/projectlyrics/server/domain/view/service/ViewCommandServiceTest.java b/src/test/java/com/projectlyrics/server/domain/view/service/ViewCommandServiceTest.java index 0bb4b133..884a017a 100644 --- a/src/test/java/com/projectlyrics/server/domain/view/service/ViewCommandServiceTest.java +++ b/src/test/java/com/projectlyrics/server/domain/view/service/ViewCommandServiceTest.java @@ -10,6 +10,7 @@ import com.projectlyrics.server.domain.note.entity.NoteBackground; import com.projectlyrics.server.domain.note.entity.NoteCreate; import com.projectlyrics.server.domain.note.entity.NoteStatus; +import com.projectlyrics.server.domain.note.entity.NoteType; import com.projectlyrics.server.domain.note.repository.NoteCommandRepository; import com.projectlyrics.server.domain.note.repository.NoteQueryRepository; import com.projectlyrics.server.domain.song.entity.Song; @@ -64,6 +65,7 @@ void setUp() { "lyrics", NoteBackground.DEFAULT, NoteStatus.PUBLISHED, + NoteType.FREE, song.getId() ); note = noteCommandRepository.save(Note.create(NoteCreate.from(noteCreateRequest, user, song))); diff --git a/src/test/java/com/projectlyrics/server/support/fixture/NoteFixture.java b/src/test/java/com/projectlyrics/server/support/fixture/NoteFixture.java index 87426f6a..1b0642e0 100644 --- a/src/test/java/com/projectlyrics/server/support/fixture/NoteFixture.java +++ b/src/test/java/com/projectlyrics/server/support/fixture/NoteFixture.java @@ -10,6 +10,7 @@ public class NoteFixture extends BaseFixture { private Long id; private String content = "노트 내용"; private NoteStatus status = NoteStatus.PUBLISHED; + private NoteType noteType = NoteType.FREE; private Lyrics lyrics = Lyrics.of("가사", NoteBackground.DEFAULT); private User publisher; private Song song; @@ -18,7 +19,7 @@ public static Note create(User publisher, Song song) { return Note.createWithId( getUniqueId(), NoteCreate.from( - new NoteCreateRequest("노트 내용", "가사", NoteBackground.DEFAULT, NoteStatus.PUBLISHED, song.getId()), + new NoteCreateRequest("노트 내용", "가사", NoteBackground.DEFAULT, NoteStatus.PUBLISHED, NoteType.FREE, song.getId()), publisher, song ) @@ -47,6 +48,11 @@ public NoteFixture status(NoteStatus status) { return this; } + public NoteFixture noteType(NoteType noteType) { + this.noteType = noteType; + return this; + } + public NoteFixture lyrics(Lyrics lyrics) { this.lyrics = lyrics; return this; @@ -66,7 +72,7 @@ public Note build() { return Note.createWithId( id, NoteCreate.from( - new NoteCreateRequest(content, lyrics.getContent(), lyrics.getBackground(), status, song.getId()), + new NoteCreateRequest(content, lyrics.getContent(), lyrics.getBackground(), status, noteType, song.getId()), publisher, song ) From 9d261ebc4086a19ca6f8ea24f83f67376e54290e Mon Sep 17 00:00:00 2001 From: "jin.geonwoo" Date: Sun, 4 Jan 2026 11:28:39 +0900 Subject: [PATCH 3/6] =?UTF-8?q?[SCRUM-261]=20feat:=20NoLyricsForNoteExcept?= =?UTF-8?q?ion=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../server/domain/common/message/ErrorCode.java | 1 + .../note/exception/NoLyricsForNoteException.java | 11 +++++++++++ 2 files changed, 12 insertions(+) create mode 100644 src/main/java/com/projectlyrics/server/domain/note/exception/NoLyricsForNoteException.java diff --git a/src/main/java/com/projectlyrics/server/domain/common/message/ErrorCode.java b/src/main/java/com/projectlyrics/server/domain/common/message/ErrorCode.java index 4e364bdd..3713e99d 100644 --- a/src/main/java/com/projectlyrics/server/domain/common/message/ErrorCode.java +++ b/src/main/java/com/projectlyrics/server/domain/common/message/ErrorCode.java @@ -59,6 +59,7 @@ public enum ErrorCode { INVALID_NOTE_DELETION(HttpStatus.BAD_REQUEST, "05003", "해당 노트를 삭제할 수 없습니다."), INVALID_NOTE_UPDATE(HttpStatus.BAD_REQUEST, "05004", "해당 노트를 수정할 수 없습니다."), TOO_MANY_DRAFT_NOTE(HttpStatus.BAD_REQUEST, "05005", "임시저장 노트의 개수가 초과되었습니다."), + NO_LYRICS_FOR_NOTE(HttpStatus.BAD_REQUEST, "05006", "해당 노트에 대한 가사가 필요합니다."), // Song SONG_NOT_FOUND(HttpStatus.NOT_FOUND, "06000", "해당 노래를 조회할 수 없습니다."), diff --git a/src/main/java/com/projectlyrics/server/domain/note/exception/NoLyricsForNoteException.java b/src/main/java/com/projectlyrics/server/domain/note/exception/NoLyricsForNoteException.java new file mode 100644 index 00000000..70b870fe --- /dev/null +++ b/src/main/java/com/projectlyrics/server/domain/note/exception/NoLyricsForNoteException.java @@ -0,0 +1,11 @@ +package com.projectlyrics.server.domain.note.exception; + +import com.projectlyrics.server.domain.common.message.ErrorCode; +import com.projectlyrics.server.global.exception.FeelinException; + +public class NoLyricsForNoteException extends FeelinException { + + public NoLyricsForNoteException() { + super(ErrorCode.NO_LYRICS_FOR_NOTE); + } +} From 924bde43b0b70406fc1f74caec18679b3d723b24 Mon Sep 17 00:00:00 2001 From: "jin.geonwoo" Date: Sun, 4 Jan 2026 11:33:15 +0900 Subject: [PATCH 4/6] =?UTF-8?q?[SCRUM-261]=20feat:=20NoteCreate.validateLy?= =?UTF-8?q?ricsForType=20=EC=B6=94=EA=B0=80=ED=95=98=EC=97=AC=20=EA=B0=80?= =?UTF-8?q?=EC=82=AC=20=ED=95=B4=EC=84=9D=20=EB=85=B8=ED=8A=B8=EC=9E=84?= =?UTF-8?q?=EC=97=90=EB=8F=84=20=EA=B0=80=EC=82=AC=20=EC=B2=A8=EB=B6=80?= =?UTF-8?q?=EB=90=98=EC=96=B4=20=EC=9E=88=EC=A7=80=20=EC=95=8A=EC=9D=80=20?= =?UTF-8?q?=EA=B2=BD=EC=9A=B0=20=EC=97=90=EB=9F=AC=20=EC=9D=BC=EC=9C=BC?= =?UTF-8?q?=ED=82=A4=EB=8F=84=EB=A1=9D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../server/domain/note/entity/NoteCreate.java | 8 +++ .../domain/note/entity/NoteCreateTest.java | 51 +++++++++++++++++++ 2 files changed, 59 insertions(+) diff --git a/src/main/java/com/projectlyrics/server/domain/note/entity/NoteCreate.java b/src/main/java/com/projectlyrics/server/domain/note/entity/NoteCreate.java index 5a55bd1d..3999ac16 100644 --- a/src/main/java/com/projectlyrics/server/domain/note/entity/NoteCreate.java +++ b/src/main/java/com/projectlyrics/server/domain/note/entity/NoteCreate.java @@ -1,6 +1,7 @@ package com.projectlyrics.server.domain.note.entity; import com.projectlyrics.server.domain.note.dto.request.NoteCreateRequest; +import com.projectlyrics.server.domain.note.exception.NoLyricsForNoteException; import com.projectlyrics.server.domain.song.entity.Song; import com.projectlyrics.server.domain.user.entity.User; @@ -21,6 +22,7 @@ public static NoteCreate from(NoteCreateRequest request, User publisher, Song so checkNull(request.noteType()); checkNull(publisher); checkNull(song); + validateLyricsForType(request.noteType(), request.lyrics()); return new NoteCreate( request.content(), @@ -32,4 +34,10 @@ public static NoteCreate from(NoteCreateRequest request, User publisher, Song so song ); } + + private static void validateLyricsForType(NoteType noteType, String lyrics) { + if (noteType == NoteType.LYRICS_ANALYSIS && (lyrics == null || lyrics.isBlank())) { + throw new NoLyricsForNoteException(); + } + } } diff --git a/src/test/java/com/projectlyrics/server/domain/note/entity/NoteCreateTest.java b/src/test/java/com/projectlyrics/server/domain/note/entity/NoteCreateTest.java index e51b8c10..44e1f366 100644 --- a/src/test/java/com/projectlyrics/server/domain/note/entity/NoteCreateTest.java +++ b/src/test/java/com/projectlyrics/server/domain/note/entity/NoteCreateTest.java @@ -4,11 +4,15 @@ import com.projectlyrics.server.domain.note.dto.request.NoteCreateRequest; import com.projectlyrics.server.domain.song.entity.Song; import com.projectlyrics.server.domain.user.entity.User; +import com.projectlyrics.server.domain.note.exception.NoLyricsForNoteException; import com.projectlyrics.server.global.exception.DomainNullFieldException; import com.projectlyrics.server.support.fixture.ArtistFixture; import com.projectlyrics.server.support.fixture.SongFixture; import com.projectlyrics.server.support.fixture.UserFixture; import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.NullAndEmptySource; +import org.junit.jupiter.params.provider.ValueSource; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatThrownBy; @@ -155,4 +159,51 @@ class NoteCreateTest { assertThatThrownBy(() -> NoteCreate.from(request, publisher, song)) .isInstanceOf(DomainNullFieldException.class); } + + @ParameterizedTest + @NullAndEmptySource + @ValueSource(strings = {" ", "\t", "\n"}) + void 가사_분석_유형인데_가사가_없으면_예외를_발생시켜야_한다(String lyrics) { + // given + Artist artist = ArtistFixture.create(); + User publisher = UserFixture.create(); + Song song = SongFixture.create(artist); + NoteCreateRequest request = new NoteCreateRequest( + "content", + lyrics, + NoteBackground.DEFAULT, + NoteStatus.PUBLISHED, + NoteType.LYRICS_ANALYSIS, + song.getId() + ); + + // when & then + assertThatThrownBy(() -> NoteCreate.from(request, publisher, song)) + .isInstanceOf(NoLyricsForNoteException.class); + } + + @Test + void 가사_분석_유형이고_가사가_있으면_노트_생성_객체를_생성해야_한다() { + // given + Artist artist = ArtistFixture.create(); + User publisher = UserFixture.create(); + Song song = SongFixture.create(artist); + NoteCreateRequest request = new NoteCreateRequest( + "content", + "lyrics", + NoteBackground.DEFAULT, + NoteStatus.PUBLISHED, + NoteType.LYRICS_ANALYSIS, + song.getId() + ); + + // when + NoteCreate result = NoteCreate.from(request, publisher, song); + + // then + assertAll( + () -> assertThat(result.noteType()).isEqualTo(NoteType.LYRICS_ANALYSIS), + () -> assertThat(result.lyrics()).isEqualTo("lyrics") + ); + } } \ No newline at end of file From c2fe276356ea061f2bb16c452d36e8b986d9e7d1 Mon Sep 17 00:00:00 2001 From: "jin.geonwoo" Date: Sun, 4 Jan 2026 11:46:43 +0900 Subject: [PATCH 5/6] =?UTF-8?q?[SCRUM-261]=20fix:=20=EC=82=AC=EC=9A=A9?= =?UTF-8?q?=EC=9E=90=EC=9D=98=20=EB=85=B8=ED=8A=B8=20=EA=B2=80=EC=83=89=20?= =?UTF-8?q?API=EC=9D=98=20URI=EB=A5=BC=20/api/v1/notes=20->=20/api/v1/user?= =?UTF-8?q?s/notes=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/note/api/NoteController.java | 20 +++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/src/main/java/com/projectlyrics/server/domain/note/api/NoteController.java b/src/main/java/com/projectlyrics/server/domain/note/api/NoteController.java index 5e1682e1..b9a8347e 100644 --- a/src/main/java/com/projectlyrics/server/domain/note/api/NoteController.java +++ b/src/main/java/com/projectlyrics/server/domain/note/api/NoteController.java @@ -29,7 +29,7 @@ import org.springframework.web.bind.annotation.RestController; @RestController -@RequestMapping("/api/v1/notes") +@RequestMapping("/api/v1") @RequiredArgsConstructor public class NoteController { @@ -37,7 +37,7 @@ public class NoteController { private final NoteQueryService noteQueryService; private final ViewCommandService viewCommandService; - @PostMapping + @PostMapping("/notes") public ResponseEntity create( @Authenticated AuthContext authContext, @RequestBody @Valid NoteCreateRequest request @@ -49,7 +49,7 @@ public ResponseEntity create( .body(new NoteCreateResponse(true)); } - @PatchMapping("/{noteId}") + @PatchMapping("/notes/{noteId}") public ResponseEntity update( @Authenticated AuthContext authContext, @PathVariable(name = "noteId") Long noteId, @@ -62,7 +62,7 @@ public ResponseEntity update( .body(new NoteUpdateResponse(true)); } - @DeleteMapping("/{noteId}") + @DeleteMapping("/notes/{noteId}") public ResponseEntity delete( @Authenticated AuthContext authContext, @PathVariable(name = "noteId") Long noteId @@ -74,7 +74,7 @@ public ResponseEntity delete( .body(new NoteDeleteResponse(true)); } - @GetMapping("/{noteId}") + @GetMapping("/notes/{noteId}") public ResponseEntity getNote( @Authenticated AuthContext authContext, @RequestHeader("Device-Id") String deviceId, @@ -91,7 +91,7 @@ public ResponseEntity getNote( .body(noteQueryService.getNoteById(noteId, authContext.getId())); } - @GetMapping + @GetMapping("/users/notes") public ResponseEntity> getNotesOfUser( @Authenticated AuthContext authContext, @RequestParam(name = "hasLyrics") boolean hasLyrics, @@ -106,7 +106,7 @@ public ResponseEntity> getNotesOfUs .body(response); } - @GetMapping("/favorite-artists") + @GetMapping("/notes/favorite-artists") public ResponseEntity> getNotesOfFavoriteArtists( @Authenticated AuthContext authContext, @RequestParam(name = "hasLyrics") boolean hasLyrics, @@ -120,7 +120,7 @@ public ResponseEntity> getNotesOfFa .body(response); } - @GetMapping("/artists") + @GetMapping("/notes/artists") public ResponseEntity> getNotesOfArtist( @Authenticated AuthContext authContext, @RequestParam(name = "hasLyrics") boolean hasLyrics, @@ -135,7 +135,7 @@ public ResponseEntity> getNotesOfAr .body(response); } - @GetMapping("/songs") + @GetMapping("/notes/songs") public ResponseEntity> getNotesOfSong( @Authenticated AuthContext authContext, @RequestParam(name = "hasLyrics") boolean hasLyrics, @@ -150,7 +150,7 @@ public ResponseEntity> getNotesOfSo .body(response); } - @GetMapping("/bookmarked") + @GetMapping("/notes/bookmarked") public ResponseEntity> getNotesBookmarked( @Authenticated AuthContext authContext, @RequestParam(name = "hasLyrics") boolean hasLyrics, From 7de4faadd8b2a980254eeff96b33e137d93cdfbe Mon Sep 17 00:00:00 2001 From: "jin.geonwoo" Date: Fri, 9 Jan 2026 16:12:53 +0900 Subject: [PATCH 6/6] =?UTF-8?q?[SCRUM-261]=20fix:=20getNotesOfFavoriteArti?= =?UTF-8?q?sts=20->=20getNotes=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/note/api/NoteController.java | 7 ++--- .../note/repository/NoteQueryRepository.java | 1 + .../impl/QueryDslNoteQueryRepository.java | 27 +++++++++++++++++++ .../domain/note/service/NoteQueryService.java | 17 +++++++----- .../domain/note/api/NoteControllerTest.java | 23 +++++++++------- .../note/service/NoteQueryServiceTest.java | 4 +-- 6 files changed, 58 insertions(+), 21 deletions(-) diff --git a/src/main/java/com/projectlyrics/server/domain/note/api/NoteController.java b/src/main/java/com/projectlyrics/server/domain/note/api/NoteController.java index b9a8347e..a245c91e 100644 --- a/src/main/java/com/projectlyrics/server/domain/note/api/NoteController.java +++ b/src/main/java/com/projectlyrics/server/domain/note/api/NoteController.java @@ -106,14 +106,15 @@ public ResponseEntity> getNotesOfUs .body(response); } - @GetMapping("/notes/favorite-artists") - public ResponseEntity> getNotesOfFavoriteArtists( + @GetMapping("/notes") + public ResponseEntity> getNotes( @Authenticated AuthContext authContext, @RequestParam(name = "hasLyrics") boolean hasLyrics, + @RequestParam(name = "isFavoriteArtistsOnly", defaultValue = "false") boolean isFavoriteArtistsOnly, @RequestParam(name = "cursor", required = false) Long cursor, @RequestParam(name = "size", defaultValue = "10") int size ) { - CursorBasePaginatedResponse response = noteQueryService.getNotesOfFavoriteArtists(hasLyrics, authContext.getId(), cursor, size); + CursorBasePaginatedResponse response = noteQueryService.getNotes(hasLyrics, isFavoriteArtistsOnly, authContext.getId(), cursor, size); return ResponseEntity .status(HttpStatus.OK) diff --git a/src/main/java/com/projectlyrics/server/domain/note/repository/NoteQueryRepository.java b/src/main/java/com/projectlyrics/server/domain/note/repository/NoteQueryRepository.java index 2b570de0..06271afb 100644 --- a/src/main/java/com/projectlyrics/server/domain/note/repository/NoteQueryRepository.java +++ b/src/main/java/com/projectlyrics/server/domain/note/repository/NoteQueryRepository.java @@ -12,6 +12,7 @@ public interface NoteQueryRepository { Slice findAllByUserId(boolean hasLyrics, Long artistId, Long userId, Long cursorId, Pageable pageable); Slice findAllByArtistIds(boolean hasLyrics, List artistsIds, Long userId, Long cursorId, Pageable pageable); + Slice findAll(boolean hasLyrics, List artistsIds, Long userId, Long cursorId, Pageable pageable); Slice findAllByArtistId(boolean hasLyrics, Long artistId, Long userId, Long cursorId, Pageable pageable); Slice findAllBookmarkedAndByArtistId(boolean hasLyrics, Long artistId, Long userId, Long cursorId, Pageable pageable); Slice findAllBySongId(boolean hasLyrics, Long songId, Long userId, Long cursorId, Pageable pageable); diff --git a/src/main/java/com/projectlyrics/server/domain/note/repository/impl/QueryDslNoteQueryRepository.java b/src/main/java/com/projectlyrics/server/domain/note/repository/impl/QueryDslNoteQueryRepository.java index 4b16294f..00f546ca 100644 --- a/src/main/java/com/projectlyrics/server/domain/note/repository/impl/QueryDslNoteQueryRepository.java +++ b/src/main/java/com/projectlyrics/server/domain/note/repository/impl/QueryDslNoteQueryRepository.java @@ -105,6 +105,33 @@ public Slice findAllByArtistIds(boolean hasLyrics, List artistsIds, return new SliceImpl<>(content, pageable, QueryDslUtils.checkIfHasNext(pageable, content)); } + @Override + public Slice findAll(boolean hasLyrics, List artistsIds, Long userId, Long cursorId, Pageable pageable) { + List content = jpaQueryFactory + .selectFrom(note) + .leftJoin(note.lyrics).fetchJoin() + .join(note.publisher).fetchJoin() + .join(note.song).fetchJoin() + .join(song.artist).fetchJoin() + .leftJoin(note.comments).fetchJoin() + .where( + hasLyrics(hasLyrics), + artistsIds == null || artistsIds.isEmpty() ? null : note.song.artist.id.in(artistsIds), + note.deletedAt.isNull(), + QueryDslUtils.ltCursorId(cursorId, note.id), + note.publisher.notIn( + JPAExpressions.select(block.blocked) + .from(block) + .where(block.blocker.id.eq(userId).and(block.deletedAt.isNull())) + ) + ) + .orderBy(note.id.desc()) + .limit(pageable.getPageSize() + 1) + .fetch(); + + return new SliceImpl<>(content, pageable, QueryDslUtils.checkIfHasNext(pageable, content)); + } + @Override public Slice findAllByArtistId(boolean hasLyrics, Long artistId, Long userId, Long cursorId, Pageable pageable) { List content = jpaQueryFactory diff --git a/src/main/java/com/projectlyrics/server/domain/note/service/NoteQueryService.java b/src/main/java/com/projectlyrics/server/domain/note/service/NoteQueryService.java index 70698e35..952c6d84 100644 --- a/src/main/java/com/projectlyrics/server/domain/note/service/NoteQueryService.java +++ b/src/main/java/com/projectlyrics/server/domain/note/service/NoteQueryService.java @@ -39,13 +39,16 @@ public CursorBasePaginatedResponse getNotesByUserId(boolean has return CursorBasePaginatedResponse.of(notes); } - public CursorBasePaginatedResponse getNotesOfFavoriteArtists(boolean hasLyrics, Long userId, Long cursor, int size) { - List artistsIds = favoriteArtistQueryRepository.findAllByUserIdFetchArtist(userId) - .stream() - .map(favoriteArtist -> favoriteArtist.getArtist().getId()) - .toList(); - - Slice notes = noteQueryRepository.findAllByArtistIds(hasLyrics, artistsIds, userId, cursor, PageRequest.ofSize(size)) + public CursorBasePaginatedResponse getNotes(boolean hasLyrics, boolean isFavoriteArtistsOnly, Long userId, Long cursor, int size) { + List artistsIds = null; + if (isFavoriteArtistsOnly) { + artistsIds = favoriteArtistQueryRepository.findAllByUserIdFetchArtist(userId) + .stream() + .map(favoriteArtist -> favoriteArtist.getArtist().getId()) + .toList(); + } + + Slice notes = noteQueryRepository.findAll(hasLyrics, artistsIds, userId, cursor, PageRequest.ofSize(size)) .map(note -> NoteGetResponse.of(note, userId)); return CursorBasePaginatedResponse.of(notes); diff --git a/src/test/java/com/projectlyrics/server/domain/note/api/NoteControllerTest.java b/src/test/java/com/projectlyrics/server/domain/note/api/NoteControllerTest.java index 7030ecb0..57b3f151 100644 --- a/src/test/java/com/projectlyrics/server/domain/note/api/NoteControllerTest.java +++ b/src/test/java/com/projectlyrics/server/domain/note/api/NoteControllerTest.java @@ -291,7 +291,7 @@ private RestDocumentationResultHandler getNoteDetailDocument() { .willReturn(response); // when, then - mockMvc.perform(get("/api/v1/notes") + mockMvc.perform(get("/api/v1/users/notes") .header(HttpHeaders.AUTHORIZATION, "Bearer " + accessToken) .param("artistId", "1") .param("hasLyrics", "false") @@ -375,7 +375,7 @@ private RestDocumentationResultHandler getNoteListOfUserDocument() { } @Test - void 사용자가_좋아하는_아티스트들과_관련된_노트_리스트를_조회하면_데이터와_200응답을_해야_한다() throws Exception { + void 노트_리스트를_조회하면_데이터와_200응답을_해야_한다() throws Exception { // given User user = UserFixture.create(); @@ -391,32 +391,37 @@ private RestDocumentationResultHandler getNoteListOfUserDocument() { data ); - given(noteQueryService.getNotesOfFavoriteArtists(anyBoolean(), any(), any(), anyInt())) + given(noteQueryService.getNotes(anyBoolean(), anyBoolean(), any(), any(), anyInt())) .willReturn(response); // when, then - mockMvc.perform(get("/api/v1/notes/favorite-artists") + mockMvc.perform(get("/api/v1/notes") .header(HttpHeaders.AUTHORIZATION, "Bearer " + accessToken) .param("hasLyrics", "false") + .param("isFavoriteArtistsOnly", "true") .param("cursor", "1") .param("size", "10") .contentType(MediaType.APPLICATION_JSON)) .andExpect(status().isOk()) - .andDo(getNoteListOfFavoriteArtistOfUserDocument()); + .andDo(getNoteListDocument()); } - private RestDocumentationResultHandler getNoteListOfFavoriteArtistOfUserDocument() { + private RestDocumentationResultHandler getNoteListDocument() { ParameterDescriptorWithType[] pagingQueryParameters = getCursorBasePagingQueryParameters(); - ParameterDescriptorWithType[] queryParams = Arrays.copyOf(pagingQueryParameters, pagingQueryParameters.length + 1); + ParameterDescriptorWithType[] queryParams = Arrays.copyOf(pagingQueryParameters, pagingQueryParameters.length + 2); queryParams[pagingQueryParameters.length] = parameterWithName("hasLyrics") .type(SimpleType.BOOLEAN) .optional() .description("가사가 있는 노트만 조회"); - + queryParams[pagingQueryParameters.length + 1] = parameterWithName("isFavoriteArtistsOnly") + .type(SimpleType.BOOLEAN) + .optional() + .description("좋아하는 아티스트의 노트만 조회"); + return restDocs.document( resource(ResourceSnippetParameters.builder() .tag("Note API") - .summary("사용자가 좋아하는 아티스트와 관련된 노트의 리스트 조회 API") + .summary("노트 리스트 조회 API") .requestHeaders(getAuthorizationHeader()) .queryParameters(queryParams) .responseFields( diff --git a/src/test/java/com/projectlyrics/server/domain/note/service/NoteQueryServiceTest.java b/src/test/java/com/projectlyrics/server/domain/note/service/NoteQueryServiceTest.java index ef2b9da0..86da0f47 100644 --- a/src/test/java/com/projectlyrics/server/domain/note/service/NoteQueryServiceTest.java +++ b/src/test/java/com/projectlyrics/server/domain/note/service/NoteQueryServiceTest.java @@ -230,7 +230,7 @@ void setUp() { Note likedArtistSongNote3 = noteCommandService.create(likedArtistSongNoteRequest, user.getId()); // when - CursorBasePaginatedResponse result = sut.getNotesOfFavoriteArtists(true, user.getId(), null, 5); + CursorBasePaginatedResponse result = sut.getNotes(true, true, user.getId(), null, 5); // then assertAll( @@ -255,7 +255,7 @@ void setUp() { blockCommandRepository.save(BlockFixture.create(user, user1)); // when - CursorBasePaginatedResponse result = sut.getNotesOfFavoriteArtists(true, user.getId(), null, 5); + CursorBasePaginatedResponse result = sut.getNotes(true, true, user.getId(), null, 5); // then assertAll(