From c12a72d4b604f1d73d2f614dfdd089b65937d8b7 Mon Sep 17 00:00:00 2001 From: Seokjin Jeon Date: Fri, 5 Apr 2024 22:15:37 +0900 Subject: [PATCH 1/2] =?UTF-8?q?[BE]=20feat:=20AdminArtistV1Controller=20?= =?UTF-8?q?=EC=95=84=ED=8B=B0=EC=8A=A4=ED=8A=B8=20=EC=A1=B0=ED=9A=8C=20?= =?UTF-8?q?=EC=9D=91=EB=8B=B5=20=ED=95=84=EB=93=9C=20=EC=88=98=EC=A0=95,?= =?UTF-8?q?=20=EA=B2=80=EC=83=89=20=EA=B8=B0=EB=8A=A5=20=ED=8E=98=EC=9D=B4?= =?UTF-8?q?=EC=A7=95=20=EC=B6=94=EA=B0=80=20(#838)=20(#840)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat: Admin 아티스트 조회 페이징 및 검색 추가 - QuerydslRepository 사용하도록 변 * refactor: ArtistRepository 사용하지 않는 메서드 삭제 * feat: 이름으로 검색할 때 글자 수가 하나이면 동등 검색하는 기능 추가 * feat: AdminArtistV1Response 필드 수정 - profileImage -> profileImageUrl 변경 - backgroundImageUrl 추가 * refactor: SearchCondition `@Nonnull` 어노테이션 추가 * refactor: SearchCondition 반환값 null 검증 제거 - null 아닌 값으로 반환 보장 --- .../AdminArtistV1QueryService.java | 21 +-- .../dto/artist/AdminArtistV1Response.java | 9 +- .../v1/AdminArtistV1Controller.java | 22 ++- .../AdminArtistV1QueryDslRepository.java | 85 +++++++++ .../artist/repository/ArtistRepository.java | 2 - .../common/querydsl/SearchCondition.java | 5 +- ...inArtistV1QueryServiceIntegrationTest.java | 177 ++++++++++++++++++ .../ArtistV1QueryServiceIntegrationTest.java | 56 ------ .../v1/AdminArtistV1ControllerTest.java | 24 +-- .../repository/MemoryArtistRepository.java | 5 - 10 files changed, 309 insertions(+), 97 deletions(-) create mode 100644 backend/src/main/java/com/festago/admin/repository/AdminArtistV1QueryDslRepository.java create mode 100644 backend/src/test/java/com/festago/admin/application/integration/AdminArtistV1QueryServiceIntegrationTest.java delete mode 100644 backend/src/test/java/com/festago/admin/application/integration/ArtistV1QueryServiceIntegrationTest.java diff --git a/backend/src/main/java/com/festago/admin/application/AdminArtistV1QueryService.java b/backend/src/main/java/com/festago/admin/application/AdminArtistV1QueryService.java index 20634fc71..e43f1c936 100644 --- a/backend/src/main/java/com/festago/admin/application/AdminArtistV1QueryService.java +++ b/backend/src/main/java/com/festago/admin/application/AdminArtistV1QueryService.java @@ -1,29 +1,28 @@ package com.festago.admin.application; import com.festago.admin.dto.artist.AdminArtistV1Response; -import com.festago.artist.domain.Artist; -import com.festago.artist.repository.ArtistRepository; -import java.util.List; +import com.festago.admin.repository.AdminArtistV1QueryDslRepository; +import com.festago.common.exception.ErrorCode; +import com.festago.common.exception.NotFoundException; +import com.festago.common.querydsl.SearchCondition; import lombok.RequiredArgsConstructor; +import org.springframework.data.domain.Page; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; -// TODO 페이징 적용 @Service @Transactional(readOnly = true) @RequiredArgsConstructor public class AdminArtistV1QueryService { - private final ArtistRepository artistRepository; + private final AdminArtistV1QueryDslRepository adminArtistV1QueryDslRepository; public AdminArtistV1Response findById(Long artistId) { - Artist artist = artistRepository.getOrThrow(artistId); - return AdminArtistV1Response.from(artist); + return adminArtistV1QueryDslRepository.findById(artistId) + .orElseThrow(() -> new NotFoundException(ErrorCode.ARTIST_NOT_FOUND)); } - public List findAll() { - return artistRepository.findAll().stream() - .map(AdminArtistV1Response::from) - .toList(); + public Page findAll(SearchCondition searchCondition) { + return adminArtistV1QueryDslRepository.findAll(searchCondition); } } diff --git a/backend/src/main/java/com/festago/admin/dto/artist/AdminArtistV1Response.java b/backend/src/main/java/com/festago/admin/dto/artist/AdminArtistV1Response.java index ebafc678b..559970a50 100644 --- a/backend/src/main/java/com/festago/admin/dto/artist/AdminArtistV1Response.java +++ b/backend/src/main/java/com/festago/admin/dto/artist/AdminArtistV1Response.java @@ -1,14 +1,15 @@ package com.festago.admin.dto.artist; -import com.festago.artist.domain.Artist; +import com.querydsl.core.annotations.QueryProjection; public record AdminArtistV1Response( Long id, String name, - String profileImage + String profileImageUrl, + String backgroundImageUrl ) { - public static AdminArtistV1Response from(Artist artist) { - return new AdminArtistV1Response(artist.getId(), artist.getName(), artist.getProfileImage()); + @QueryProjection + public AdminArtistV1Response { } } diff --git a/backend/src/main/java/com/festago/admin/presentation/v1/AdminArtistV1Controller.java b/backend/src/main/java/com/festago/admin/presentation/v1/AdminArtistV1Controller.java index 61ec73e96..51bc30488 100644 --- a/backend/src/main/java/com/festago/admin/presentation/v1/AdminArtistV1Controller.java +++ b/backend/src/main/java/com/festago/admin/presentation/v1/AdminArtistV1Controller.java @@ -5,11 +5,15 @@ import com.festago.admin.dto.artist.ArtistV1CreateRequest; import com.festago.admin.dto.artist.ArtistV1UpdateRequest; import com.festago.artist.application.ArtistCommandService; +import com.festago.common.aop.ValidPageable; +import com.festago.common.querydsl.SearchCondition; import io.swagger.v3.oas.annotations.Hidden; import jakarta.validation.Valid; import java.net.URI; -import java.util.List; import lombok.RequiredArgsConstructor; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; +import org.springframework.data.web.PageableDefault; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.DeleteMapping; import org.springframework.web.bind.annotation.GetMapping; @@ -18,6 +22,7 @@ import org.springframework.web.bind.annotation.PutMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; @RestController @@ -53,14 +58,19 @@ public ResponseEntity delete(@PathVariable Long artistId) { @GetMapping("/{artistId}") public ResponseEntity findById(@PathVariable Long artistId) { - AdminArtistV1Response response = artistV1QueryService.findById(artistId); - return ResponseEntity.ok(response); + return ResponseEntity.ok() + .body(artistV1QueryService.findById(artistId)); } @GetMapping - public ResponseEntity> findAll() { - List response = artistV1QueryService.findAll(); - return ResponseEntity.ok(response); + @ValidPageable(maxSize = 50) + public ResponseEntity> findAll( + @RequestParam(defaultValue = "") String searchFilter, + @RequestParam(defaultValue = "") String searchKeyword, + @PageableDefault(size = 10) Pageable pageable + ) { + return ResponseEntity.ok() + .body(artistV1QueryService.findAll(new SearchCondition(searchFilter, searchKeyword, pageable))); } } diff --git a/backend/src/main/java/com/festago/admin/repository/AdminArtistV1QueryDslRepository.java b/backend/src/main/java/com/festago/admin/repository/AdminArtistV1QueryDslRepository.java new file mode 100644 index 000000000..40ec467e6 --- /dev/null +++ b/backend/src/main/java/com/festago/admin/repository/AdminArtistV1QueryDslRepository.java @@ -0,0 +1,85 @@ +package com.festago.admin.repository; + +import static com.festago.artist.domain.QArtist.artist; + +import com.festago.admin.dto.artist.AdminArtistV1Response; +import com.festago.admin.dto.artist.QAdminArtistV1Response; +import com.festago.artist.domain.Artist; +import com.festago.common.querydsl.QueryDslRepositorySupport; +import com.festago.common.querydsl.SearchCondition; +import com.querydsl.core.types.dsl.BooleanExpression; +import java.util.Optional; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; +import org.springframework.stereotype.Repository; +import org.springframework.util.StringUtils; + +@Repository +public class AdminArtistV1QueryDslRepository extends QueryDslRepositorySupport { + + public AdminArtistV1QueryDslRepository() { + super(Artist.class); + } + + public Page findAll(SearchCondition searchCondition) { + Pageable pageable = searchCondition.pageable(); + return applyPagination(pageable, + queryFactory -> queryFactory.select( + new QAdminArtistV1Response( + artist.id, + artist.name, + artist.profileImage, + artist.backgroundImageUrl + )) + .from(artist) + .where(applySearch(searchCondition)) + .offset(pageable.getOffset()) + .limit(pageable.getPageSize()), + queryFactory -> queryFactory.select(artist.count()) + .where(applySearch(searchCondition)) + .from(artist)); + } + + private BooleanExpression applySearch(SearchCondition searchCondition) { + String searchFilter = searchCondition.searchFilter(); + String searchKeyword = searchCondition.searchKeyword(); + return switch (searchFilter) { + case "id" -> eqId(searchKeyword); + case "name" -> searchKeyword.length() == 1 ? eqName(searchKeyword) : containsName(searchKeyword); + default -> null; + }; + } + + private BooleanExpression eqId(String id) { + if (StringUtils.hasText(id)) { + return artist.id.stringValue().eq(id); + } + return null; + } + + private BooleanExpression eqName(String name) { + if (StringUtils.hasText(name)) { + return artist.name.eq(name); + } + return null; + } + + private BooleanExpression containsName(String name) { + if (StringUtils.hasText(name)) { + return artist.name.contains(name); + } + return null; + } + + public Optional findById(Long artistId) { + return fetchOne(queryFactory -> queryFactory.select( + new QAdminArtistV1Response( + artist.id, + artist.name, + artist.profileImage, + artist.backgroundImageUrl + )) + .from(artist) + .where(artist.id.eq(artistId))); + } +} diff --git a/backend/src/main/java/com/festago/artist/repository/ArtistRepository.java b/backend/src/main/java/com/festago/artist/repository/ArtistRepository.java index a0db11fa8..daaac169b 100644 --- a/backend/src/main/java/com/festago/artist/repository/ArtistRepository.java +++ b/backend/src/main/java/com/festago/artist/repository/ArtistRepository.java @@ -21,8 +21,6 @@ default Artist getOrThrow(Long artistId) { Optional findById(Long id); - List findAll(); - long countByIdIn(List artistIds); List findByIdIn(Collection artistIds); diff --git a/backend/src/main/java/com/festago/common/querydsl/SearchCondition.java b/backend/src/main/java/com/festago/common/querydsl/SearchCondition.java index 547baef8d..9f7e73fc5 100644 --- a/backend/src/main/java/com/festago/common/querydsl/SearchCondition.java +++ b/backend/src/main/java/com/festago/common/querydsl/SearchCondition.java @@ -1,23 +1,26 @@ package com.festago.common.querydsl; import com.festago.common.util.Validator; +import jakarta.annotation.Nonnull; import org.springframework.data.domain.Pageable; public record SearchCondition( String searchFilter, String searchKeyword, - Pageable pageable + @Nonnull Pageable pageable ) { public SearchCondition { Validator.notNull(pageable, "pageable"); } + @Nonnull @Override public String searchFilter() { return searchFilter != null ? searchFilter : ""; } + @Nonnull @Override public String searchKeyword() { return searchKeyword != null ? searchKeyword : ""; diff --git a/backend/src/test/java/com/festago/admin/application/integration/AdminArtistV1QueryServiceIntegrationTest.java b/backend/src/test/java/com/festago/admin/application/integration/AdminArtistV1QueryServiceIntegrationTest.java new file mode 100644 index 000000000..48c920093 --- /dev/null +++ b/backend/src/test/java/com/festago/admin/application/integration/AdminArtistV1QueryServiceIntegrationTest.java @@ -0,0 +1,177 @@ +package com.festago.admin.application.integration; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.SoftAssertions.assertSoftly; + +import com.festago.admin.application.AdminArtistV1QueryService; +import com.festago.admin.dto.artist.AdminArtistV1Response; +import com.festago.artist.domain.Artist; +import com.festago.artist.repository.ArtistRepository; +import com.festago.common.querydsl.SearchCondition; +import com.festago.support.ApplicationIntegrationTest; +import com.festago.support.fixture.ArtistFixture; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayNameGeneration; +import org.junit.jupiter.api.DisplayNameGenerator; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.domain.PageRequest; +import org.springframework.data.domain.Pageable; +import org.springframework.data.domain.Sort; + +@DisplayNameGeneration(DisplayNameGenerator.ReplaceUnderscores.class) +@SuppressWarnings("NonAsciiCharacters") +class AdminArtistV1QueryServiceIntegrationTest extends ApplicationIntegrationTest { + + @Autowired + AdminArtistV1QueryService adminArtistV1QueryService; + + @Autowired + ArtistRepository artistRepository; + + @Test + void 아티스트를_단건_조회한다() { + // given + Artist expected = artistRepository.save(ArtistFixture.builder().build()); + + // when + var actual = adminArtistV1QueryService.findById(expected.getId()); + + // then + assertSoftly(softly -> { + softly.assertThat(actual.id()).isEqualTo(expected.getId()); + softly.assertThat(actual.name()).isEqualTo(expected.getName()); + softly.assertThat(actual.profileImageUrl()).isEqualTo(expected.getProfileImage()); + softly.assertThat(actual.backgroundImageUrl()).isEqualTo(expected.getBackgroundImageUrl()); + }); + } + + @Nested + class findAll { + + Artist 벤; + Artist 베토벤; + Artist 아이유; + Artist 에픽하이; + Artist 소녀시대; + + @BeforeEach + void setUp() { + 벤 = artistRepository.save(ArtistFixture.builder() + .name("벤") + .build()); + 베토벤 = artistRepository.save(ArtistFixture.builder() + .name("베토벤") + .build()); + 아이유 = artistRepository.save(ArtistFixture.builder() + .name("아이유") + .build()); + 에픽하이 = artistRepository.save(ArtistFixture.builder() + .name("에픽하이") + .build()); + 소녀시대 = artistRepository.save(ArtistFixture.builder() + .name("소녀시대") + .build()); + } + + @Test + void 정렬이_되어야_한다() { + // given + Pageable pageable = PageRequest.of(0, 10, Sort.by(Sort.Direction.ASC, "name")); + SearchCondition searchCondition = new SearchCondition("", "", pageable); + + // when + var response = adminArtistV1QueryService.findAll(searchCondition); + + assertThat(response.getContent()) + .map(AdminArtistV1Response::name) + .containsExactly(베토벤.getName(), 벤.getName(), 소녀시대.getName(), 아이유.getName(), 에픽하이.getName()); + } + + @Test + void 식별자로_검색이_되어야_한다() { + // given + Pageable pageable = Pageable.ofSize(10); + SearchCondition searchCondition = new SearchCondition("id", 소녀시대.getId().toString(), pageable); + + // when + var response = adminArtistV1QueryService.findAll(searchCondition); + + assertThat(response.getContent()) + .map(AdminArtistV1Response::name) + .containsExactlyInAnyOrder(소녀시대.getName()); + } + + @Test + void 이름이_포함된_검색이_되어야_한다() { + // given + Pageable pageable = Pageable.ofSize(10); + SearchCondition searchCondition = new SearchCondition("name", "에픽", pageable); + + // when + var response = adminArtistV1QueryService.findAll(searchCondition); + + assertThat(response.getContent()) + .map(AdminArtistV1Response::name) + .containsExactly(에픽하이.getName()); + } + + @Test + void 이름으로_검색할때_한_글자이면_동등_검색이_되어야_한다() { + // given + Pageable pageable = Pageable.ofSize(10); + SearchCondition searchCondition = new SearchCondition("name", "벤", pageable); + + // when + var response = adminArtistV1QueryService.findAll(searchCondition); + + assertThat(response.getContent()) + .map(AdminArtistV1Response::name) + .containsExactly(벤.getName()); + } + + @Test + void 검색_필터가_비어있으면_필터링이_적용되지_않는다() { + // given + Pageable pageable = Pageable.ofSize(10); + SearchCondition searchCondition = new SearchCondition("", "글렌", pageable); + + // when + var response = adminArtistV1QueryService.findAll(searchCondition); + + assertThat(response.getContent()) + .hasSize(5); + } + + @Test + void 검색어가_비어있으면_필터링이_적용되지_않는다() { + // given + Pageable pageable = Pageable.ofSize(10); + SearchCondition searchCondition = new SearchCondition("id", "", pageable); + + // when + var response = adminArtistV1QueryService.findAll(searchCondition); + + assertThat(response.getContent()) + .hasSize(5); + } + + @Test + void 페이지네이션이_적용_되어야_한다() { + // given + Pageable pageable = PageRequest.of(0, 2); + SearchCondition searchCondition = new SearchCondition("", "", pageable); + + // when + var response = adminArtistV1QueryService.findAll(searchCondition); + + // then + assertSoftly(softly -> { + softly.assertThat(response.getSize()).isEqualTo(2); + softly.assertThat(response.getTotalPages()).isEqualTo(3); + softly.assertThat(response.getTotalElements()).isEqualTo(5); + }); + } + } +} diff --git a/backend/src/test/java/com/festago/admin/application/integration/ArtistV1QueryServiceIntegrationTest.java b/backend/src/test/java/com/festago/admin/application/integration/ArtistV1QueryServiceIntegrationTest.java deleted file mode 100644 index aaaca88e7..000000000 --- a/backend/src/test/java/com/festago/admin/application/integration/ArtistV1QueryServiceIntegrationTest.java +++ /dev/null @@ -1,56 +0,0 @@ -package com.festago.admin.application.integration; - -import static org.assertj.core.api.Assertions.assertThat; - -import com.festago.admin.application.AdminArtistV1QueryService; -import com.festago.admin.dto.artist.AdminArtistV1Response; -import com.festago.artist.domain.Artist; -import com.festago.artist.repository.ArtistRepository; -import com.festago.support.ApplicationIntegrationTest; -import com.festago.support.fixture.ArtistFixture; -import java.util.List; -import org.junit.jupiter.api.DisplayNameGeneration; -import org.junit.jupiter.api.DisplayNameGenerator; -import org.junit.jupiter.api.Test; -import org.springframework.beans.factory.annotation.Autowired; - -@DisplayNameGeneration(DisplayNameGenerator.ReplaceUnderscores.class) -@SuppressWarnings("NonAsciiCharacters") -class ArtistV1QueryServiceIntegrationTest extends ApplicationIntegrationTest { - - @Autowired - AdminArtistV1QueryService adminArtistV1QueryService; - - @Autowired - ArtistRepository artistRepository; - - @Test - void 아티스트를_단건_조회한다() { - // given - Artist expected = artistRepository.save(ArtistFixture.builder().build()); - - // when - AdminArtistV1Response actual = adminArtistV1QueryService.findById(expected.getId()); - - // then - assertThat(actual).usingRecursiveComparison() - .isEqualTo(expected); - } - - @Test - void 아티스트_정보를_변경한다() { - // given - List expected = List.of( - artistRepository.save(ArtistFixture.builder().name("윤하").build()), - artistRepository.save(ArtistFixture.builder().name("Lana del rey").build()), - artistRepository.save(ArtistFixture.builder().name("악동뮤지션").build()) - ); - - // when - List actual = adminArtistV1QueryService.findAll(); - - // then - assertThat(actual).usingRecursiveComparison() - .isEqualTo(expected); - } -} diff --git a/backend/src/test/java/com/festago/admin/presentation/v1/AdminArtistV1ControllerTest.java b/backend/src/test/java/com/festago/admin/presentation/v1/AdminArtistV1ControllerTest.java index 1de59b865..b66486349 100644 --- a/backend/src/test/java/com/festago/admin/presentation/v1/AdminArtistV1ControllerTest.java +++ b/backend/src/test/java/com/festago/admin/presentation/v1/AdminArtistV1ControllerTest.java @@ -1,13 +1,13 @@ package com.festago.admin.presentation.v1; import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyLong; import static org.mockito.BDDMockito.given; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.delete; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.put; import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print; -import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.header; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; @@ -19,6 +19,7 @@ import com.festago.artist.application.ArtistCommandService; import com.festago.artist.dto.command.ArtistCreateCommand; import com.festago.auth.domain.Role; +import com.festago.common.querydsl.SearchCondition; import com.festago.support.CustomWebMvcTest; import com.festago.support.WithMockAuth; import jakarta.servlet.http.Cookie; @@ -29,6 +30,7 @@ import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.domain.PageImpl; import org.springframework.http.HttpHeaders; import org.springframework.http.MediaType; import org.springframework.test.web.servlet.MockMvc; @@ -189,8 +191,8 @@ class 올바른_주소로 { @WithMockAuth(role = Role.ADMIN) void 요청을_보내면_200_응답과_body가_반환된다() throws Exception { // given - AdminArtistV1Response expected = new AdminArtistV1Response(1L, "윤하", "https://image.com/image.png"); - given(adminArtistV1QueryService.findById(expected.id())) + var expected = new AdminArtistV1Response(1L, "윤하", "https://image.com/image.png", "https://image.com/background.png"); + given(adminArtistV1QueryService.findById(anyLong())) .willReturn(expected); // when & then @@ -198,8 +200,7 @@ class 올바른_주소로 { .cookie(TOKEN_COOKIE) .contentType(MediaType.APPLICATION_JSON)) .andDo(print()) - .andExpect(status().isOk()) - .andExpect(content().json(objectMapper.writeValueAsString(expected))); + .andExpect(status().isOk()); } @Test @@ -233,20 +234,19 @@ class 올바른_주소로 { @WithMockAuth(role = Role.ADMIN) void 요청을_보내면_200_응답과_body가_반환된다() throws Exception { // given - List expected = List.of( - new AdminArtistV1Response(1L, "윤하", "https://image.com/image1.png"), - new AdminArtistV1Response(2L, "고윤하", "https://image.com/image2.png") + var expected = List.of( + new AdminArtistV1Response(1L, "윤하", "https://image.com/image1.png", "https://image.com/background1.png"), + new AdminArtistV1Response(2L, "고윤하", "https://image.com/image2.png", "https://image.com/background2.png") ); - given(adminArtistV1QueryService.findAll()) - .willReturn(expected); + given(adminArtistV1QueryService.findAll(any(SearchCondition.class))) + .willReturn(new PageImpl<>(expected)); // when & then mockMvc.perform(get(uri) .cookie(TOKEN_COOKIE) .contentType(MediaType.APPLICATION_JSON)) .andDo(print()) - .andExpect(status().isOk()) - .andExpect(content().json(objectMapper.writeValueAsString(expected))); + .andExpect(status().isOk()); } @Test diff --git a/backend/src/test/java/com/festago/artist/repository/MemoryArtistRepository.java b/backend/src/test/java/com/festago/artist/repository/MemoryArtistRepository.java index 0382002b9..7dc5ec658 100644 --- a/backend/src/test/java/com/festago/artist/repository/MemoryArtistRepository.java +++ b/backend/src/test/java/com/festago/artist/repository/MemoryArtistRepository.java @@ -39,11 +39,6 @@ public Optional findById(Long id) { return Optional.ofNullable(memory.get(id)); } - @Override - public List findAll() { - return memory.values().stream().toList(); - } - @Override public long countByIdIn(List artistIds) { return memory.values().stream() From 835a6e1d4033c70416f12517b7f8d33d228a7ae3 Mon Sep 17 00:00:00 2001 From: SeongHoonC <108349655+SeongHoonC@users.noreply.github.com> Date: Sun, 7 Apr 2024 23:27:02 +0900 Subject: [PATCH 2/2] =?UTF-8?q?[AN/USER]=20feat:=20=ED=85=8C=EC=8A=A4?= =?UTF-8?q?=ED=8A=B8=20=EC=84=9C=EB=B2=84=EC=99=80=20=EC=97=B0=EA=B2=B0?= =?UTF-8?q?=ED=95=9C=EB=8B=A4(#830)=20(#834)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * refactor(repository): Default 가 접두사가 되도록 변경 * refactor(FestivalSearch): 축제 검색 객체 재생성 * refactor(response): API 명세에 맞게 이름 수정 * refactor(Module): 검색 원격 저장소 주입 * fest(Module): 아티스트 저장소 주입 * fest(Festival): 축제는 학교를 가지지 않을 수 있다 * fest(artist): 아티스트 상세 응답 받기 객체 정의 * fest(festival): 7 이하는 분홍색으로 표시한다 * fest(ArtistDetailViewModel): 데이터를 비동기로 요청한다 * fest(FestivalResponse): 학교를 가지지 않을 수 있다 * refactor(ArtistDetailViewModel): 아티스트 상세 비동기 호출 코드 정리 * refactor(ArtistSearchViewHolder): 공연 개수 길이에 따라 색깔이 바뀐다 * refactor(RepositoryModule): 축제 저장소 주입 이름 변경 Co-authored-by: 해시 <67777523+EmilyCh0@users.noreply.github.com> --------- Co-authored-by: 해시 <67777523+EmilyCh0@users.noreply.github.com> --- .../di/singletonscope/RepositoryModule.kt | 16 ++++---- .../data/di/singletonscope/ServiceModule.kt | 18 +++++++++ .../data/dto/artist/ArtistDetailResponse.kt | 22 +++++++++++ .../festago/data/dto/artist/ArtistResponse.kt | 4 +- .../data/dto/artist/ArtistSearchResponse.kt | 4 +- .../data/dto/festival/FestivalResponse.kt | 4 +- .../dto/festival/FestivalSearchResponse.kt | 25 ++++++++++++ .../festago/data/dto/school/SchoolResponse.kt | 4 +- .../SchoolFestivalArtistResponse.kt | 2 +- .../repository/DefaultArtistRepository.kt | 38 +++++++++++++++++++ ...sitory.kt => DefaultFestivalRepository.kt} | 2 +- ...pository.kt => DefaultSchoolRepository.kt} | 2 +- .../repository/DefaultSearchRepository.kt | 4 +- .../data/repository/FakeArtistRepository.kt | 14 +++++-- .../data/repository/FakeSearchRepository.kt | 6 +-- .../data/service/ArtistRetrofitService.kt | 26 +++++++++++++ .../data/service/SearchRetrofitService.kt | 4 +- .../domain/model/artist/ArtistDetail.kt | 6 ++- .../domain/model/artist/ArtistMedia.kt | 8 ---- .../festago/domain/model/festival/Festival.kt | 2 +- .../domain/model/search/FestivalSearch.kt | 13 +++++++ .../domain/repository/ArtistRepository.kt | 9 ++++- .../domain/repository/SearchRepository.kt | 4 +- .../ui/artistdetail/ArtistDetailViewModel.kt | 7 +++- .../ArtistDetailFestivalViewHolder.kt | 2 +- .../ui/artistdetail/uistate/SchoolUiState.kt | 6 --- .../festivallist/FestivalListViewModel.kt | 5 --- .../FestivalListFestivalViewHolder.kt | 2 +- .../uistate/FestivalItemUiState.kt | 1 - .../SchoolDetailFestivalViewHolder.kt | 4 +- .../ui/schooldetail/SchoolDetailViewModel.kt | 7 +--- .../uistate/FestivalItemUiState.kt | 1 - .../presentation/ui/search/SearchViewModel.kt | 9 +---- .../artistsearch/ArtistSearchViewHolder.kt | 2 +- .../uistate/FestivalSearchItemUiState.kt | 1 - .../res/layout/fragment_artist_detail.xml | 2 +- 36 files changed, 207 insertions(+), 79 deletions(-) create mode 100644 android/festago/data/src/main/java/com/festago/festago/data/dto/artist/ArtistDetailResponse.kt create mode 100644 android/festago/data/src/main/java/com/festago/festago/data/dto/festival/FestivalSearchResponse.kt create mode 100644 android/festago/data/src/main/java/com/festago/festago/data/repository/DefaultArtistRepository.kt rename android/festago/data/src/main/java/com/festago/festago/data/repository/{FestivalDefaultRepository.kt => DefaultFestivalRepository.kt} (97%) rename android/festago/data/src/main/java/com/festago/festago/data/repository/{SchoolDefaultRepository.kt => DefaultSchoolRepository.kt} (96%) create mode 100644 android/festago/data/src/main/java/com/festago/festago/data/service/ArtistRetrofitService.kt delete mode 100644 android/festago/domain/src/main/java/com/festago/festago/domain/model/artist/ArtistMedia.kt create mode 100644 android/festago/domain/src/main/java/com/festago/festago/domain/model/search/FestivalSearch.kt delete mode 100644 android/festago/presentation/src/main/java/com/festago/festago/presentation/ui/artistdetail/uistate/SchoolUiState.kt diff --git a/android/festago/data/src/main/java/com/festago/festago/data/di/singletonscope/RepositoryModule.kt b/android/festago/data/src/main/java/com/festago/festago/data/di/singletonscope/RepositoryModule.kt index 556bcf3da..4849994fc 100644 --- a/android/festago/data/src/main/java/com/festago/festago/data/di/singletonscope/RepositoryModule.kt +++ b/android/festago/data/src/main/java/com/festago/festago/data/di/singletonscope/RepositoryModule.kt @@ -1,10 +1,10 @@ package com.festago.festago.data.di.singletonscope +import com.festago.festago.data.repository.DefaultArtistRepository +import com.festago.festago.data.repository.DefaultFestivalRepository import com.festago.festago.data.repository.DefaultRecentSearchRepository -import com.festago.festago.data.repository.FakeArtistRepository -import com.festago.festago.data.repository.FakeFestivalRepository -import com.festago.festago.data.repository.FakeSchoolRepository -import com.festago.festago.data.repository.FakeSearchRepository +import com.festago.festago.data.repository.DefaultSchoolRepository +import com.festago.festago.data.repository.DefaultSearchRepository import com.festago.festago.domain.repository.ArtistRepository import com.festago.festago.domain.repository.FestivalRepository import com.festago.festago.domain.repository.RecentSearchRepository @@ -22,15 +22,15 @@ interface RepositoryModule { @Binds @Singleton - fun bindsFestivalDefaultRepository(festivalRepository: FakeFestivalRepository): FestivalRepository + fun bindsFestivalRepository(festivalRepository: DefaultFestivalRepository): FestivalRepository @Binds @Singleton - fun bindsArtistRepository(artistRepository: FakeArtistRepository): ArtistRepository + fun bindsArtistRepository(artistRepository: DefaultArtistRepository): ArtistRepository @Binds @Singleton - fun bindsSchoolRepository(schoolRepository: FakeSchoolRepository): SchoolRepository + fun bindsSchoolRepository(schoolRepository: DefaultSchoolRepository): SchoolRepository @Binds @Singleton @@ -38,5 +38,5 @@ interface RepositoryModule { @Binds @Singleton - fun bindsSearchRepository(searchRepository: FakeSearchRepository): SearchRepository + fun bindsSearchRepository(searchRepository: DefaultSearchRepository): SearchRepository } diff --git a/android/festago/data/src/main/java/com/festago/festago/data/di/singletonscope/ServiceModule.kt b/android/festago/data/src/main/java/com/festago/festago/data/di/singletonscope/ServiceModule.kt index 0dc3d28e4..3c60c80e0 100644 --- a/android/festago/data/src/main/java/com/festago/festago/data/di/singletonscope/ServiceModule.kt +++ b/android/festago/data/src/main/java/com/festago/festago/data/di/singletonscope/ServiceModule.kt @@ -1,7 +1,9 @@ package com.festago.festago.data.di.singletonscope +import com.festago.festago.data.service.ArtistRetrofitService import com.festago.festago.data.service.FestivalRetrofitService import com.festago.festago.data.service.SchoolRetrofitService +import com.festago.festago.data.service.SearchRetrofitService import dagger.Module import dagger.Provides import dagger.hilt.InstallIn @@ -21,6 +23,14 @@ object ServiceModule { return retrofit.create(FestivalRetrofitService::class.java) } + @Provides + @Singleton + fun providesArtistRetrofitService( + @NormalRetrofitQualifier retrofit: Retrofit, + ): ArtistRetrofitService { + return retrofit.create(ArtistRetrofitService::class.java) + } + @Provides @Singleton fun providesSchoolRetrofitService( @@ -28,4 +38,12 @@ object ServiceModule { ): SchoolRetrofitService { return retrofit.create(SchoolRetrofitService::class.java) } + + @Provides + @Singleton + fun providesSearchRetrofitService( + @NormalRetrofitQualifier retrofit: Retrofit, + ): SearchRetrofitService { + return retrofit.create(SearchRetrofitService::class.java) + } } diff --git a/android/festago/data/src/main/java/com/festago/festago/data/dto/artist/ArtistDetailResponse.kt b/android/festago/data/src/main/java/com/festago/festago/data/dto/artist/ArtistDetailResponse.kt new file mode 100644 index 000000000..3025956a9 --- /dev/null +++ b/android/festago/data/src/main/java/com/festago/festago/data/dto/artist/ArtistDetailResponse.kt @@ -0,0 +1,22 @@ +package com.festago.festago.data.dto.artist + +import com.festago.festago.data.dto.school.SocialMediaResponse +import com.festago.festago.domain.model.artist.ArtistDetail +import kotlinx.serialization.Serializable + +@Serializable +data class ArtistDetailResponse( + val id: Int, + val name: String?, + val profileImageUrl: String?, + val backgroundImageUrl: String?, + val socialMedias: List, +) { + fun toDomain() = ArtistDetail( + id = id, + artistName = name ?: "", + profileUrl = profileImageUrl ?: "", + backgroundUrl = backgroundImageUrl ?: "", + artistMedia = socialMedias.map { it.toDomain() }, + ) +} diff --git a/android/festago/data/src/main/java/com/festago/festago/data/dto/artist/ArtistResponse.kt b/android/festago/data/src/main/java/com/festago/festago/data/dto/artist/ArtistResponse.kt index 2ff020431..f87a1026d 100644 --- a/android/festago/data/src/main/java/com/festago/festago/data/dto/artist/ArtistResponse.kt +++ b/android/festago/data/src/main/java/com/festago/festago/data/dto/artist/ArtistResponse.kt @@ -7,11 +7,11 @@ import kotlinx.serialization.Serializable data class ArtistResponse( val id: Long, val name: String, - val profileImage: String, + val profileImageUrl: String, ) { fun toDomain() = Artist( id = id, name = name, - imageUrl = profileImage, + imageUrl = profileImageUrl, ) } diff --git a/android/festago/data/src/main/java/com/festago/festago/data/dto/artist/ArtistSearchResponse.kt b/android/festago/data/src/main/java/com/festago/festago/data/dto/artist/ArtistSearchResponse.kt index 098e28470..99e4db4fb 100644 --- a/android/festago/data/src/main/java/com/festago/festago/data/dto/artist/ArtistSearchResponse.kt +++ b/android/festago/data/src/main/java/com/festago/festago/data/dto/artist/ArtistSearchResponse.kt @@ -9,13 +9,13 @@ data class ArtistSearchResponse( val name: String, val profileImageUrl: String, val todayStage: Int, - val upcomingStage: Int, + val plannedStage: Int, ) { fun toDomain() = ArtistSearch( id = id, name = name, profileImageUrl = profileImageUrl, todayStage = todayStage, - upcomingStage = upcomingStage, + upcomingStage = plannedStage, ) } diff --git a/android/festago/data/src/main/java/com/festago/festago/data/dto/festival/FestivalResponse.kt b/android/festago/data/src/main/java/com/festago/festago/data/dto/festival/FestivalResponse.kt index 8cbfda71b..65ca92527 100644 --- a/android/festago/data/src/main/java/com/festago/festago/data/dto/festival/FestivalResponse.kt +++ b/android/festago/data/src/main/java/com/festago/festago/data/dto/festival/FestivalResponse.kt @@ -13,7 +13,7 @@ data class FestivalResponse( val startDate: String, val endDate: String, val posterImageUrl: String, - val school: SchoolResponse, + val school: SchoolResponse? = null, val artists: List, ) { fun toDomain(): Festival = Festival( @@ -22,7 +22,7 @@ data class FestivalResponse( startDate = LocalDate.parse(startDate), endDate = LocalDate.parse(endDate), imageUrl = posterImageUrl, - school = school.toDomain(), + school = school?.toDomain(), artists = artists.map { it.toDomain() }, ) } diff --git a/android/festago/data/src/main/java/com/festago/festago/data/dto/festival/FestivalSearchResponse.kt b/android/festago/data/src/main/java/com/festago/festago/data/dto/festival/FestivalSearchResponse.kt new file mode 100644 index 000000000..acb3a9f52 --- /dev/null +++ b/android/festago/data/src/main/java/com/festago/festago/data/dto/festival/FestivalSearchResponse.kt @@ -0,0 +1,25 @@ +package com.festago.festago.data.dto.festival + +import com.festago.festago.data.dto.artist.ArtistResponse +import com.festago.festago.domain.model.search.FestivalSearch +import kotlinx.serialization.Serializable +import java.time.LocalDate + +@Serializable +data class FestivalSearchResponse( + val id: Long, + val name: String, + val startDate: String, + val endDate: String, + val posterImageUrl: String, + val artists: List, +) { + fun toDomain(): FestivalSearch = FestivalSearch( + id = id, + name = name, + startDate = LocalDate.parse(startDate), + endDate = LocalDate.parse(endDate), + imageUrl = posterImageUrl, + artists = artists.map { it.toDomain() }, + ) +} diff --git a/android/festago/data/src/main/java/com/festago/festago/data/dto/school/SchoolResponse.kt b/android/festago/data/src/main/java/com/festago/festago/data/dto/school/SchoolResponse.kt index 3ea9ba949..70d309900 100644 --- a/android/festago/data/src/main/java/com/festago/festago/data/dto/school/SchoolResponse.kt +++ b/android/festago/data/src/main/java/com/festago/festago/data/dto/school/SchoolResponse.kt @@ -7,11 +7,11 @@ import kotlinx.serialization.Serializable data class SchoolResponse( val id: Long, val name: String, - val imageUrl: String = "", + val profileImageUrl: String = "", ) { fun toDomain() = School( id = id, name = name, - imageUrl = imageUrl, + imageUrl = profileImageUrl, ) } diff --git a/android/festago/data/src/main/java/com/festago/festago/data/dto/schooldetail/SchoolFestivalArtistResponse.kt b/android/festago/data/src/main/java/com/festago/festago/data/dto/schooldetail/SchoolFestivalArtistResponse.kt index da5132deb..60c187114 100644 --- a/android/festago/data/src/main/java/com/festago/festago/data/dto/schooldetail/SchoolFestivalArtistResponse.kt +++ b/android/festago/data/src/main/java/com/festago/festago/data/dto/schooldetail/SchoolFestivalArtistResponse.kt @@ -7,7 +7,7 @@ import kotlinx.serialization.Serializable data class SchoolFestivalArtistResponse( val id: Int, val name: String, - val profileImageUrl: String + val profileImageUrl: String, ) { fun toDomain() = Artist( id = id.toLong(), diff --git a/android/festago/data/src/main/java/com/festago/festago/data/repository/DefaultArtistRepository.kt b/android/festago/data/src/main/java/com/festago/festago/data/repository/DefaultArtistRepository.kt new file mode 100644 index 000000000..845213614 --- /dev/null +++ b/android/festago/data/src/main/java/com/festago/festago/data/repository/DefaultArtistRepository.kt @@ -0,0 +1,38 @@ +package com.festago.festago.data.repository + +import com.festago.festago.data.service.ArtistRetrofitService +import com.festago.festago.data.util.onSuccessOrCatch +import com.festago.festago.data.util.runCatchingResponse +import com.festago.festago.domain.model.artist.ArtistDetail +import com.festago.festago.domain.model.festival.FestivalsPage +import com.festago.festago.domain.repository.ArtistRepository +import java.time.LocalDate +import javax.inject.Inject + +class DefaultArtistRepository @Inject constructor( + private val artistRetrofitService: ArtistRetrofitService, +) : ArtistRepository { + + override suspend fun loadArtistDetail(id: Long): Result { + return runCatchingResponse { artistRetrofitService.getArtistDetail(id) } + .onSuccessOrCatch { it.toDomain() } + } + + override suspend fun loadArtistFestivals( + id: Long, + size: Int?, + lastFestivalId: Long?, + lastStartDate: LocalDate?, + isPast: Boolean?, + ): Result { + return runCatchingResponse { + artistRetrofitService.getArtistFestivals( + artistId = id, + size = size, + lastFestivalId = lastFestivalId, + lastStartDate = lastStartDate, + isPast = isPast, + ) + }.onSuccessOrCatch { it.toDomain() } + } +} diff --git a/android/festago/data/src/main/java/com/festago/festago/data/repository/FestivalDefaultRepository.kt b/android/festago/data/src/main/java/com/festago/festago/data/repository/DefaultFestivalRepository.kt similarity index 97% rename from android/festago/data/src/main/java/com/festago/festago/data/repository/FestivalDefaultRepository.kt rename to android/festago/data/src/main/java/com/festago/festago/data/repository/DefaultFestivalRepository.kt index 8b8dee5c1..28cda3afc 100644 --- a/android/festago/data/src/main/java/com/festago/festago/data/repository/FestivalDefaultRepository.kt +++ b/android/festago/data/src/main/java/com/festago/festago/data/repository/DefaultFestivalRepository.kt @@ -12,7 +12,7 @@ import com.festago.festago.domain.repository.FestivalRepository import java.time.LocalDate import javax.inject.Inject -class FestivalDefaultRepository @Inject constructor( +class DefaultFestivalRepository @Inject constructor( private val festivalRetrofitService: FestivalRetrofitService, ) : FestivalRepository { diff --git a/android/festago/data/src/main/java/com/festago/festago/data/repository/SchoolDefaultRepository.kt b/android/festago/data/src/main/java/com/festago/festago/data/repository/DefaultSchoolRepository.kt similarity index 96% rename from android/festago/data/src/main/java/com/festago/festago/data/repository/SchoolDefaultRepository.kt rename to android/festago/data/src/main/java/com/festago/festago/data/repository/DefaultSchoolRepository.kt index 9de6c9139..8bd0e488e 100644 --- a/android/festago/data/src/main/java/com/festago/festago/data/repository/SchoolDefaultRepository.kt +++ b/android/festago/data/src/main/java/com/festago/festago/data/repository/DefaultSchoolRepository.kt @@ -9,7 +9,7 @@ import com.festago.festago.domain.repository.SchoolRepository import java.time.LocalDate import javax.inject.Inject -class SchoolDefaultRepository @Inject constructor( +class DefaultSchoolRepository @Inject constructor( private val schoolRetrofitService: SchoolRetrofitService ) : SchoolRepository { override suspend fun loadSchoolInfo(schoolId: Long): Result { diff --git a/android/festago/data/src/main/java/com/festago/festago/data/repository/DefaultSearchRepository.kt b/android/festago/data/src/main/java/com/festago/festago/data/repository/DefaultSearchRepository.kt index 322fbcb23..b3288ad5c 100644 --- a/android/festago/data/src/main/java/com/festago/festago/data/repository/DefaultSearchRepository.kt +++ b/android/festago/data/src/main/java/com/festago/festago/data/repository/DefaultSearchRepository.kt @@ -3,8 +3,8 @@ package com.festago.festago.data.repository import com.festago.festago.data.service.SearchRetrofitService import com.festago.festago.data.util.onSuccessOrCatch import com.festago.festago.data.util.runCatchingResponse -import com.festago.festago.domain.model.festival.Festival import com.festago.festago.domain.model.search.ArtistSearch +import com.festago.festago.domain.model.search.FestivalSearch import com.festago.festago.domain.model.search.SchoolSearch import com.festago.festago.domain.repository.SearchRepository import javax.inject.Inject @@ -13,7 +13,7 @@ class DefaultSearchRepository @Inject constructor( private val searchRetrofitService: SearchRetrofitService, ) : SearchRepository { - override suspend fun searchFestivals(searchQuery: String): Result> { + override suspend fun searchFestivals(searchQuery: String): Result> { return runCatchingResponse { searchRetrofitService.searchFestivals(searchQuery) }.onSuccessOrCatch { festivalResponses -> festivalResponses.map { it.toDomain() } } diff --git a/android/festago/data/src/main/java/com/festago/festago/data/repository/FakeArtistRepository.kt b/android/festago/data/src/main/java/com/festago/festago/data/repository/FakeArtistRepository.kt index 9e9ccefe9..2b01044d6 100644 --- a/android/festago/data/src/main/java/com/festago/festago/data/repository/FakeArtistRepository.kt +++ b/android/festago/data/src/main/java/com/festago/festago/data/repository/FakeArtistRepository.kt @@ -2,10 +2,10 @@ package com.festago.festago.data.repository import com.festago.festago.domain.model.artist.Artist import com.festago.festago.domain.model.artist.ArtistDetail -import com.festago.festago.domain.model.artist.ArtistMedia import com.festago.festago.domain.model.festival.Festival import com.festago.festago.domain.model.festival.FestivalsPage import com.festago.festago.domain.model.school.School +import com.festago.festago.domain.model.social.SocialMedia import com.festago.festago.domain.repository.ArtistRepository import java.time.LocalDate import javax.inject.Inject @@ -21,13 +21,13 @@ class FakeArtistRepository @Inject constructor() : ArtistRepository { "https://static.wikia.nocookie.net/witchers/images/d/d9/New_Jeans_Cover.png/revision/latest?cb=20220801091438", "https://static.wikia.nocookie.net/witchers/images/d/d9/New_Jeans_Cover.png/revision/latest?cb=20220801091438", listOf( - ArtistMedia( + SocialMedia( "https://upload.wikimedia.org/wikipedia/commons/thumb/a/a5/Instagram_icon.png/1200px-Instagram_icon.png?20200512141346", "공식 인스타그램", "https://upload.wikimedia.org/wikipedia/commons/thumb/e/e7/Instagram_logo_2016.svg/264px-Instagram_logo_2016.svg.png", "https://www.instagram.com/newjeans_official/", ), - ArtistMedia( + SocialMedia( "https://upload.wikimedia.org/wikipedia/commons/thumb/c/ce/X_logo_2023.svg/600px-X_logo_2023.svg.png?20230819000805", "공식 엑스", "https://upload.wikimedia.org/wikipedia/commons/thumb/c/ce/X_logo_2023.svg/531px-X_logo_2023.svg.png", @@ -37,7 +37,13 @@ class FakeArtistRepository @Inject constructor() : ArtistRepository { ), ) - override suspend fun loadArtistFestivals(id: Long, size: Int): Result = + override suspend fun loadArtistFestivals( + id: Long, + size: Int?, + lastFestivalId: Long?, + lastStartDate: LocalDate?, + isPast: Boolean?, + ): Result = Result.success( FestivalsPage( isLastPage = false, diff --git a/android/festago/data/src/main/java/com/festago/festago/data/repository/FakeSearchRepository.kt b/android/festago/data/src/main/java/com/festago/festago/data/repository/FakeSearchRepository.kt index bb269124b..efe801fc0 100644 --- a/android/festago/data/src/main/java/com/festago/festago/data/repository/FakeSearchRepository.kt +++ b/android/festago/data/src/main/java/com/festago/festago/data/repository/FakeSearchRepository.kt @@ -1,7 +1,7 @@ package com.festago.festago.data.repository -import com.festago.festago.domain.model.festival.Festival import com.festago.festago.domain.model.search.ArtistSearch +import com.festago.festago.domain.model.search.FestivalSearch import com.festago.festago.domain.model.search.SchoolSearch import com.festago.festago.domain.repository.SearchRepository import kotlinx.coroutines.delay @@ -10,11 +10,11 @@ import javax.inject.Inject class FakeSearchRepository @Inject constructor() : SearchRepository { private var times = 0 - override suspend fun searchFestivals(searchQuery: String): Result> { + override suspend fun searchFestivals(searchQuery: String): Result> { delay(1000) times++ if (times % 2 == 0) { - return Result.success(FakeFestivals.popularFestivals) + return Result.success(listOf()) } return Result.success(listOf()) } diff --git a/android/festago/data/src/main/java/com/festago/festago/data/service/ArtistRetrofitService.kt b/android/festago/data/src/main/java/com/festago/festago/data/service/ArtistRetrofitService.kt new file mode 100644 index 000000000..d8fc53a16 --- /dev/null +++ b/android/festago/data/src/main/java/com/festago/festago/data/service/ArtistRetrofitService.kt @@ -0,0 +1,26 @@ +package com.festago.festago.data.service + +import com.festago.festago.data.dto.artist.ArtistDetailResponse +import com.festago.festago.data.dto.festival.FestivalsResponse +import retrofit2.Response +import retrofit2.http.GET +import retrofit2.http.Path +import retrofit2.http.Query +import java.time.LocalDate + +interface ArtistRetrofitService { + + @GET("api/v1/artists/{artistId}/festivals") + suspend fun getArtistFestivals( + @Path("artistId") artistId: Long, + @Query("size") size: Int?, + @Query("lastFestivalId") lastFestivalId: Long?, + @Query("lastStartDate") lastStartDate: LocalDate?, + @Query("isPast") isPast: Boolean?, + ): Response + + @GET("api/v1/artists/{artistId}") + suspend fun getArtistDetail( + @Path("artistId") artistId: Long, + ): Response +} diff --git a/android/festago/data/src/main/java/com/festago/festago/data/service/SearchRetrofitService.kt b/android/festago/data/src/main/java/com/festago/festago/data/service/SearchRetrofitService.kt index 794b43f9d..6f525f989 100644 --- a/android/festago/data/src/main/java/com/festago/festago/data/service/SearchRetrofitService.kt +++ b/android/festago/data/src/main/java/com/festago/festago/data/service/SearchRetrofitService.kt @@ -1,7 +1,7 @@ package com.festago.festago.data.service import com.festago.festago.data.dto.artist.ArtistSearchResponse -import com.festago.festago.data.dto.festival.FestivalResponse +import com.festago.festago.data.dto.festival.FestivalSearchResponse import com.festago.festago.data.dto.school.SchoolSearchResponse import retrofit2.Response import retrofit2.http.GET @@ -11,7 +11,7 @@ interface SearchRetrofitService { @GET("api/v1/search/festivals") suspend fun searchFestivals( @Query("keyword") keyword: String, - ): Response> + ): Response> @GET("api/v1/search/artists") suspend fun searchArtists( diff --git a/android/festago/domain/src/main/java/com/festago/festago/domain/model/artist/ArtistDetail.kt b/android/festago/domain/src/main/java/com/festago/festago/domain/model/artist/ArtistDetail.kt index c47db4176..69e40b2ae 100644 --- a/android/festago/domain/src/main/java/com/festago/festago/domain/model/artist/ArtistDetail.kt +++ b/android/festago/domain/src/main/java/com/festago/festago/domain/model/artist/ArtistDetail.kt @@ -1,9 +1,11 @@ package com.festago.festago.domain.model.artist +import com.festago.festago.domain.model.social.SocialMedia + data class ArtistDetail( val id: Int, val artistName: String, - val logoUrl: String, + val profileUrl: String, val backgroundUrl: String, - val artistMedia: List, + val artistMedia: List, ) diff --git a/android/festago/domain/src/main/java/com/festago/festago/domain/model/artist/ArtistMedia.kt b/android/festago/domain/src/main/java/com/festago/festago/domain/model/artist/ArtistMedia.kt deleted file mode 100644 index c2508219b..000000000 --- a/android/festago/domain/src/main/java/com/festago/festago/domain/model/artist/ArtistMedia.kt +++ /dev/null @@ -1,8 +0,0 @@ -package com.festago.festago.domain.model.artist - -data class ArtistMedia( - val type: String, - val name: String, - val logoUrl: String, - val url: String, -) diff --git a/android/festago/domain/src/main/java/com/festago/festago/domain/model/festival/Festival.kt b/android/festago/domain/src/main/java/com/festago/festago/domain/model/festival/Festival.kt index d1e0912db..cdc882891 100644 --- a/android/festago/domain/src/main/java/com/festago/festago/domain/model/festival/Festival.kt +++ b/android/festago/domain/src/main/java/com/festago/festago/domain/model/festival/Festival.kt @@ -10,6 +10,6 @@ data class Festival( val startDate: LocalDate, val endDate: LocalDate, val imageUrl: String, - val school: School, + val school: School?, val artists: List, ) diff --git a/android/festago/domain/src/main/java/com/festago/festago/domain/model/search/FestivalSearch.kt b/android/festago/domain/src/main/java/com/festago/festago/domain/model/search/FestivalSearch.kt new file mode 100644 index 000000000..00d06acd2 --- /dev/null +++ b/android/festago/domain/src/main/java/com/festago/festago/domain/model/search/FestivalSearch.kt @@ -0,0 +1,13 @@ +package com.festago.festago.domain.model.search + +import com.festago.festago.domain.model.artist.Artist +import java.time.LocalDate + +data class FestivalSearch( + val id: Long, + val name: String, + val startDate: LocalDate, + val endDate: LocalDate, + val imageUrl: String, + val artists: List, +) diff --git a/android/festago/domain/src/main/java/com/festago/festago/domain/repository/ArtistRepository.kt b/android/festago/domain/src/main/java/com/festago/festago/domain/repository/ArtistRepository.kt index a49d3f194..31afb9b17 100644 --- a/android/festago/domain/src/main/java/com/festago/festago/domain/repository/ArtistRepository.kt +++ b/android/festago/domain/src/main/java/com/festago/festago/domain/repository/ArtistRepository.kt @@ -2,9 +2,16 @@ package com.festago.festago.domain.repository import com.festago.festago.domain.model.artist.ArtistDetail import com.festago.festago.domain.model.festival.FestivalsPage +import java.time.LocalDate interface ArtistRepository { suspend fun loadArtistDetail(id: Long): Result - suspend fun loadArtistFestivals(id: Long, size: Int): Result + suspend fun loadArtistFestivals( + id: Long, + size: Int? = null, + lastFestivalId: Long? = null, + lastStartDate: LocalDate? = null, + isPast: Boolean? = null, + ): Result } diff --git a/android/festago/domain/src/main/java/com/festago/festago/domain/repository/SearchRepository.kt b/android/festago/domain/src/main/java/com/festago/festago/domain/repository/SearchRepository.kt index b2129f848..276707831 100644 --- a/android/festago/domain/src/main/java/com/festago/festago/domain/repository/SearchRepository.kt +++ b/android/festago/domain/src/main/java/com/festago/festago/domain/repository/SearchRepository.kt @@ -1,11 +1,11 @@ package com.festago.festago.domain.repository -import com.festago.festago.domain.model.festival.Festival import com.festago.festago.domain.model.search.ArtistSearch +import com.festago.festago.domain.model.search.FestivalSearch import com.festago.festago.domain.model.search.SchoolSearch interface SearchRepository { - suspend fun searchFestivals(searchQuery: String): Result> + suspend fun searchFestivals(searchQuery: String): Result> suspend fun searchArtists(searchQuery: String): Result> suspend fun searchSchools(searchQuery: String): Result> } diff --git a/android/festago/presentation/src/main/java/com/festago/festago/presentation/ui/artistdetail/ArtistDetailViewModel.kt b/android/festago/presentation/src/main/java/com/festago/festago/presentation/ui/artistdetail/ArtistDetailViewModel.kt index 503779bd7..0b33d2a78 100644 --- a/android/festago/presentation/src/main/java/com/festago/festago/presentation/ui/artistdetail/ArtistDetailViewModel.kt +++ b/android/festago/presentation/src/main/java/com/festago/festago/presentation/ui/artistdetail/ArtistDetailViewModel.kt @@ -8,6 +8,7 @@ import com.festago.festago.presentation.ui.artistdetail.uistate.ArtistDetailUiSt import com.festago.festago.presentation.ui.artistdetail.uistate.ArtistUiState import com.festago.festago.presentation.ui.artistdetail.uistate.FestivalItemUiState import dagger.hilt.android.lifecycle.HiltViewModel +import kotlinx.coroutines.async import kotlinx.coroutines.flow.MutableSharedFlow import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.SharedFlow @@ -33,9 +34,11 @@ class ArtistDetailViewModel @Inject constructor( viewModelScope.launch { runCatching { + val deferredArtistDetail = async { artistRepository.loadArtistDetail(id) } + val deferredFestivals = async { artistRepository.loadArtistFestivals(id, 10) } _uiState.value = ArtistDetailUiState.Success( - artistRepository.loadArtistDetail(id).getOrThrow(), - artistRepository.loadArtistFestivals(id, 20).getOrThrow().toUiState(), + deferredArtistDetail.await().getOrThrow(), + deferredFestivals.await().getOrThrow().toUiState(), ) }.onFailure { _uiState.value = ArtistDetailUiState.Error diff --git a/android/festago/presentation/src/main/java/com/festago/festago/presentation/ui/artistdetail/adapter/festival/ArtistDetailFestivalViewHolder.kt b/android/festago/presentation/src/main/java/com/festago/festago/presentation/ui/artistdetail/adapter/festival/ArtistDetailFestivalViewHolder.kt index 35696e39c..59999b4c4 100644 --- a/android/festago/presentation/src/main/java/com/festago/festago/presentation/ui/artistdetail/adapter/festival/ArtistDetailFestivalViewHolder.kt +++ b/android/festago/presentation/src/main/java/com/festago/festago/presentation/ui/artistdetail/adapter/festival/ArtistDetailFestivalViewHolder.kt @@ -47,7 +47,7 @@ class ArtistDetailFestivalViewHolder(private val binding: ItemArtistDetailFestiv ) } - LocalDate.now() == item.startDate.minusDays(1) -> { + LocalDate.now() >= item.startDate.minusDays(7) -> { dDayView.text = context.getString( R.string.artist_detail_tv_dday_format, item.startDate.compareTo(LocalDate.now()).toString(), diff --git a/android/festago/presentation/src/main/java/com/festago/festago/presentation/ui/artistdetail/uistate/SchoolUiState.kt b/android/festago/presentation/src/main/java/com/festago/festago/presentation/ui/artistdetail/uistate/SchoolUiState.kt deleted file mode 100644 index d8f34a721..000000000 --- a/android/festago/presentation/src/main/java/com/festago/festago/presentation/ui/artistdetail/uistate/SchoolUiState.kt +++ /dev/null @@ -1,6 +0,0 @@ -package com.festago.festago.presentation.ui.artistdetail.uistate - -data class SchoolUiState( - val id: Long, - val name: String, -) diff --git a/android/festago/presentation/src/main/java/com/festago/festago/presentation/ui/home/festivallist/FestivalListViewModel.kt b/android/festago/presentation/src/main/java/com/festago/festago/presentation/ui/home/festivallist/FestivalListViewModel.kt index 2b96deedd..6a380b740 100644 --- a/android/festago/presentation/src/main/java/com/festago/festago/presentation/ui/home/festivallist/FestivalListViewModel.kt +++ b/android/festago/presentation/src/main/java/com/festago/festago/presentation/ui/home/festivallist/FestivalListViewModel.kt @@ -13,7 +13,6 @@ import com.festago.festago.presentation.ui.home.festivallist.uistate.FestivalFil import com.festago.festago.presentation.ui.home.festivallist.uistate.FestivalItemUiState import com.festago.festago.presentation.ui.home.festivallist.uistate.FestivalListUiState import com.festago.festago.presentation.ui.home.festivallist.uistate.PopularFestivalUiState -import com.festago.festago.presentation.ui.home.festivallist.uistate.SchoolUiState import dagger.hilt.android.lifecycle.HiltViewModel import kotlinx.coroutines.async import kotlinx.coroutines.flow.MutableSharedFlow @@ -152,10 +151,6 @@ class FestivalListViewModel @Inject constructor( startDate = startDate, endDate = endDate, imageUrl = imageUrl, - schoolUiState = SchoolUiState( - id = school.id, - name = school.name, - ), artists = artists.map { artist -> ArtistUiState(artist.id, artist.name, artist.imageUrl) }, diff --git a/android/festago/presentation/src/main/java/com/festago/festago/presentation/ui/home/festivallist/festival/FestivalListFestivalViewHolder.kt b/android/festago/presentation/src/main/java/com/festago/festago/presentation/ui/home/festivallist/festival/FestivalListFestivalViewHolder.kt index 8ff856c0b..3ef86e217 100644 --- a/android/festago/presentation/src/main/java/com/festago/festago/presentation/ui/home/festivallist/festival/FestivalListFestivalViewHolder.kt +++ b/android/festago/presentation/src/main/java/com/festago/festago/presentation/ui/home/festivallist/festival/FestivalListFestivalViewHolder.kt @@ -49,7 +49,7 @@ class FestivalListFestivalViewHolder( ) } - LocalDate.now() == item.startDate.minusDays(1) -> { + LocalDate.now() >= item.startDate.minusDays(7) -> { dDayView.setTextColor(context.getColor(R.color.background_gray_01)) dDayView.text = context.getString( R.string.festival_list_tv_dday_format, diff --git a/android/festago/presentation/src/main/java/com/festago/festago/presentation/ui/home/festivallist/uistate/FestivalItemUiState.kt b/android/festago/presentation/src/main/java/com/festago/festago/presentation/ui/home/festivallist/uistate/FestivalItemUiState.kt index ba83253a2..5f35701f1 100644 --- a/android/festago/presentation/src/main/java/com/festago/festago/presentation/ui/home/festivallist/uistate/FestivalItemUiState.kt +++ b/android/festago/presentation/src/main/java/com/festago/festago/presentation/ui/home/festivallist/uistate/FestivalItemUiState.kt @@ -8,7 +8,6 @@ data class FestivalItemUiState( val startDate: LocalDate, val endDate: LocalDate, val imageUrl: String, - val schoolUiState: SchoolUiState, val artists: List, val onFestivalDetail: (festivalId: Long) -> Unit, ) diff --git a/android/festago/presentation/src/main/java/com/festago/festago/presentation/ui/schooldetail/SchoolDetailFestivalViewHolder.kt b/android/festago/presentation/src/main/java/com/festago/festago/presentation/ui/schooldetail/SchoolDetailFestivalViewHolder.kt index 8c145006c..e16c9bf28 100644 --- a/android/festago/presentation/src/main/java/com/festago/festago/presentation/ui/schooldetail/SchoolDetailFestivalViewHolder.kt +++ b/android/festago/presentation/src/main/java/com/festago/festago/presentation/ui/schooldetail/SchoolDetailFestivalViewHolder.kt @@ -15,7 +15,7 @@ import com.festago.festago.presentation.ui.schooldetail.uistate.FestivalItemUiSt import java.time.LocalDate class SchoolDetailFestivalViewHolder( - private val binding: ItemSchoolDetailFestivalBinding + private val binding: ItemSchoolDetailFestivalBinding, ) : RecyclerView.ViewHolder(binding.root) { private val artistAdapter = ArtistAdapter() @@ -46,7 +46,7 @@ class SchoolDetailFestivalViewHolder( ) } - LocalDate.now() == item.startDate.minusDays(1) -> { + LocalDate.now() >= item.startDate.minusDays(7) -> { dDayView.setTextColor(context.getColor(R.color.background_gray_01)) dDayView.text = context.getString( R.string.festival_list_tv_dday_format, diff --git a/android/festago/presentation/src/main/java/com/festago/festago/presentation/ui/schooldetail/SchoolDetailViewModel.kt b/android/festago/presentation/src/main/java/com/festago/festago/presentation/ui/schooldetail/SchoolDetailViewModel.kt index 7f4804934..562fc90f2 100644 --- a/android/festago/presentation/src/main/java/com/festago/festago/presentation/ui/schooldetail/SchoolDetailViewModel.kt +++ b/android/festago/presentation/src/main/java/com/festago/festago/presentation/ui/schooldetail/SchoolDetailViewModel.kt @@ -9,7 +9,6 @@ import com.festago.festago.domain.repository.SchoolRepository import com.festago.festago.presentation.ui.schooldetail.uistate.ArtistUiState import com.festago.festago.presentation.ui.schooldetail.uistate.FestivalItemUiState import com.festago.festago.presentation.ui.schooldetail.uistate.SchoolDetailUiState -import com.festago.festago.presentation.ui.schooldetail.uistate.SchoolUiState import dagger.hilt.android.lifecycle.HiltViewModel import kotlinx.coroutines.async import kotlinx.coroutines.flow.MutableStateFlow @@ -39,7 +38,7 @@ class SchoolDetailViewModel @Inject constructor( _uiState.value = SchoolDetailUiState.Success( schoolInfo = schoolInfo, festivals = festivalPage.festivals.map { it.toUiState() }, - isLast = festivalPage.isLastPage + isLast = festivalPage.isLastPage, ) }.onFailure { _uiState.value = SchoolDetailUiState.Error @@ -57,10 +56,6 @@ class SchoolDetailViewModel @Inject constructor( startDate = startDate, endDate = endDate, imageUrl = imageUrl, - schoolUiState = SchoolUiState( - id = school.id, - name = school.name, - ), artists = artists.map { artist -> ArtistUiState(artist.id, artist.name, artist.imageUrl) }, diff --git a/android/festago/presentation/src/main/java/com/festago/festago/presentation/ui/schooldetail/uistate/FestivalItemUiState.kt b/android/festago/presentation/src/main/java/com/festago/festago/presentation/ui/schooldetail/uistate/FestivalItemUiState.kt index 626a3bd97..246165d63 100644 --- a/android/festago/presentation/src/main/java/com/festago/festago/presentation/ui/schooldetail/uistate/FestivalItemUiState.kt +++ b/android/festago/presentation/src/main/java/com/festago/festago/presentation/ui/schooldetail/uistate/FestivalItemUiState.kt @@ -8,6 +8,5 @@ data class FestivalItemUiState( val startDate: LocalDate, val endDate: LocalDate, val imageUrl: String, - val schoolUiState: SchoolUiState, val artists: List, ) diff --git a/android/festago/presentation/src/main/java/com/festago/festago/presentation/ui/search/SearchViewModel.kt b/android/festago/presentation/src/main/java/com/festago/festago/presentation/ui/search/SearchViewModel.kt index 58e02f39d..74a950c19 100644 --- a/android/festago/presentation/src/main/java/com/festago/festago/presentation/ui/search/SearchViewModel.kt +++ b/android/festago/presentation/src/main/java/com/festago/festago/presentation/ui/search/SearchViewModel.kt @@ -4,9 +4,9 @@ import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import com.festago.festago.common.analytics.AnalyticsHelper import com.festago.festago.common.analytics.logNetworkFailure -import com.festago.festago.domain.model.festival.Festival import com.festago.festago.domain.model.recentsearch.RecentSearchQuery import com.festago.festago.domain.model.search.ArtistSearch +import com.festago.festago.domain.model.search.FestivalSearch import com.festago.festago.domain.model.search.SchoolSearch import com.festago.festago.domain.repository.RecentSearchRepository import com.festago.festago.domain.repository.SearchRepository @@ -17,7 +17,6 @@ import com.festago.festago.presentation.ui.search.uistate.ArtistUiState import com.festago.festago.presentation.ui.search.uistate.FestivalSearchItemUiState import com.festago.festago.presentation.ui.search.uistate.RecentSearchItemUiState import com.festago.festago.presentation.ui.search.uistate.SchoolSearchItemUiState -import com.festago.festago.presentation.ui.search.uistate.SchoolUiState import com.festago.festago.presentation.ui.search.uistate.SearchUiState import dagger.hilt.android.lifecycle.HiltViewModel import kotlinx.coroutines.async @@ -102,16 +101,12 @@ class SearchViewModel @Inject constructor( onRecentSearchDeleted = ::deleteRecentSearch, ) - private fun Festival.toUiState() = FestivalSearchItemUiState( + private fun FestivalSearch.toUiState() = FestivalSearchItemUiState( id = id, name = name, startDate = startDate, endDate = endDate, imageUrl = imageUrl, - schoolUiState = SchoolUiState( - id = school.id, - name = school.name, - ), artists = artists.map { artist -> ArtistUiState(artist.id, artist.name, artist.imageUrl, ::showArtistDetail) }, diff --git a/android/festago/presentation/src/main/java/com/festago/festago/presentation/ui/search/artistsearch/ArtistSearchViewHolder.kt b/android/festago/presentation/src/main/java/com/festago/festago/presentation/ui/search/artistsearch/ArtistSearchViewHolder.kt index 5f74472e8..01318c966 100644 --- a/android/festago/presentation/src/main/java/com/festago/festago/presentation/ui/search/artistsearch/ArtistSearchViewHolder.kt +++ b/android/festago/presentation/src/main/java/com/festago/festago/presentation/ui/search/artistsearch/ArtistSearchViewHolder.kt @@ -34,7 +34,7 @@ class ArtistSearchViewHolder( text = SpannableString(stageCountText).apply { getPartialColorText( start = COLOR_INDEX, - end = COLOR_INDEX + 1, + end = COLOR_INDEX + count.toString().length, color = context.getColor(R.color.secondary_pink_01), ) } diff --git a/android/festago/presentation/src/main/java/com/festago/festago/presentation/ui/search/uistate/FestivalSearchItemUiState.kt b/android/festago/presentation/src/main/java/com/festago/festago/presentation/ui/search/uistate/FestivalSearchItemUiState.kt index 218141b52..89876b681 100644 --- a/android/festago/presentation/src/main/java/com/festago/festago/presentation/ui/search/uistate/FestivalSearchItemUiState.kt +++ b/android/festago/presentation/src/main/java/com/festago/festago/presentation/ui/search/uistate/FestivalSearchItemUiState.kt @@ -8,7 +8,6 @@ data class FestivalSearchItemUiState( val startDate: LocalDate, val endDate: LocalDate, val imageUrl: String, - val schoolUiState: SchoolUiState, val artists: List, val onFestivalSearchClick: (festivalId: Long) -> Unit, ) diff --git a/android/festago/presentation/src/main/res/layout/fragment_artist_detail.xml b/android/festago/presentation/src/main/res/layout/fragment_artist_detail.xml index 9c9daa8b9..1d756777b 100644 --- a/android/festago/presentation/src/main/res/layout/fragment_artist_detail.xml +++ b/android/festago/presentation/src/main/res/layout/fragment_artist_detail.xml @@ -69,7 +69,7 @@