Skip to content

Commit

Permalink
feat: 관리자용 토픽 조회 API 구현 (#307)
Browse files Browse the repository at this point in the history
  • Loading branch information
kdkdhoho committed Oct 27, 2024
1 parent ccd07f7 commit 739763b
Show file tree
Hide file tree
Showing 6 changed files with 216 additions and 8 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import com.listywave.topic.application.domain.Topic;
import com.listywave.topic.application.service.dto.ExposedTopicFindResponse;
import com.listywave.topic.application.service.dto.TopicCreateRequest;
import com.listywave.topic.application.service.dto.TopicFindResponse;
import com.listywave.topic.repository.TopicRepository;
import com.listywave.user.application.domain.User;
import com.listywave.user.repository.user.UserRepository;
Expand Down Expand Up @@ -30,4 +31,10 @@ public ExposedTopicFindResponse findAllExposed(@Nullable Long cursorId, int size
List<Topic> result = topicRepository.findAllExposed(cursorId, size);
return ExposedTopicFindResponse.of(result, size);
}

public TopicFindResponse findAll(@Nullable Long cursorId, int size) {
List<Topic> result = topicRepository.findAll(cursorId, size);
long totalCount = (topicRepository.count() / size) + 1;
return TopicFindResponse.from(result, size, totalCount);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
package com.listywave.topic.application.service.dto;

import com.listywave.topic.application.domain.Topic;
import java.time.LocalDateTime;
import java.util.List;
import lombok.Builder;

@Builder
public record TopicFindResponse(
boolean hasNext,
long totalCount,
Long cursorId,
List<TopicDto> topics
) {

public static TopicFindResponse from(List<Topic> topics, int size, long totalCount) {
if (topics.isEmpty()) {
return new TopicFindResponse(false, totalCount, null, List.of());
}

boolean hasNext = false;
if (topics.size() > size) {
hasNext = true;
topics.remove(topics.size() - 1);
}
long cursorId = topics.get(topics.size() - 1).getId();

return TopicFindResponse.builder()
.hasNext(hasNext)
.totalCount(totalCount)
.cursorId(cursorId)
.topics(TopicDto.toList(topics))
.build();
}

@Builder
public record TopicDto(
String categoryEngName,
String categoryKorName,
String title,
String description,
LocalDateTime createdDate,
Long ownerId,
String ownerNickname,
boolean isAnonymous,
boolean isExposed
) {

public static List<TopicDto> toList(List<Topic> topics) {
return topics.stream()
.map(TopicDto::of)
.toList();
}

private static TopicDto of(Topic topic) {
return TopicDto.builder()
.categoryEngName(topic.getCategory().name())
.categoryKorName(topic.getCategory().getViewName())
.title(topic.getTitle().getValue())
.description(topic.getDescription().getValue())
.createdDate(topic.getCreatedDate())
.ownerId(topic.getUser().getId())
.ownerNickname(topic.getUser().getNickname())
.isAnonymous(topic.isAnonymous())
.isExposed(topic.isExposed())
.build();
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import com.listywave.topic.application.service.TopicService;
import com.listywave.topic.application.service.dto.ExposedTopicFindResponse;
import com.listywave.topic.application.service.dto.TopicCreateRequest;
import com.listywave.topic.application.service.dto.TopicFindResponse;
import lombok.RequiredArgsConstructor;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
Expand Down Expand Up @@ -32,4 +33,13 @@ ResponseEntity<ExposedTopicFindResponse> findAllExposed(
ExposedTopicFindResponse result = topicService.findAllExposed(cursorId, size);
return ResponseEntity.ok(result);
}

@GetMapping("/admin/topics")
ResponseEntity<TopicFindResponse> findAll(
@RequestParam(required = false) Long cursorId,
@RequestParam(defaultValue = "5") int size
) {
TopicFindResponse result = topicService.findAll(cursorId, size);
return ResponseEntity.ok(result);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,6 @@
public interface CustomTopicRepository {

List<Topic> findAllExposed(Long cursorId, int size);

List<Topic> findAll(Long cursorId, int size);
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
import com.listywave.topic.application.domain.Topic;
import com.querydsl.core.types.dsl.BooleanExpression;
import com.querydsl.jpa.impl.JPAQueryFactory;
import jakarta.annotation.Nullable;
import java.util.List;
import lombok.RequiredArgsConstructor;

Expand All @@ -15,7 +16,7 @@ public class CustomTopicRepositoryImpl implements CustomTopicRepository {
private final JPAQueryFactory queryFactory;

@Override
public List<Topic> findAllExposed(Long cursorId, int size) {
public List<Topic> findAllExposed(@Nullable Long cursorId, int size) {
return queryFactory
.selectFrom(topic)
.join(user).on(topic.user.id.eq(user.id))
Expand All @@ -28,7 +29,24 @@ public List<Topic> findAllExposed(Long cursorId, int size) {
.fetch();
}

private static BooleanExpression cursorIdLowerThan(Long cursorId) {
private BooleanExpression cursorIdLowerThan(Long cursorId) {
return cursorId == null ? null : topic.id.lt(cursorId);
}

@Override
public List<Topic> findAll(@Nullable Long cursorId, int size) {
return queryFactory
.selectFrom(topic)
.join(user).on(topic.user.id.eq(user.id))
.where(
cursorIdGreaterThan(cursorId)
)
.limit(size + 1)
.orderBy(topic.id.asc())
.fetch();
}

private BooleanExpression cursorIdGreaterThan(Long cursorId) {
return cursorId == null ? null : topic.id.gt(cursorId);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
import com.listywave.topic.application.service.dto.ExposedTopicFindResponse;
import com.listywave.topic.application.service.dto.ExposedTopicFindResponse.TopicDto;
import com.listywave.topic.application.service.dto.TopicCreateRequest;
import com.listywave.topic.application.service.dto.TopicFindResponse;
import java.util.List;
import org.junit.jupiter.api.Nested;
import org.junit.jupiter.api.Test;
Expand Down Expand Up @@ -47,7 +48,7 @@ class 토픽_생성 {
}

@Nested
class 토픽_조회 {
class 사용자용_토픽_조회 {

@Test
void 노출이_승인된_토픽만_조회한다() {
Expand Down Expand Up @@ -124,7 +125,7 @@ class 토픽_조회 {
}

@Test
void cursorId가_5이고_size가_5일_때_노출이_승인된_토픽을_조회한다() {
void cursorId가_뒤에서_다섯_번째고_size가_5일_때_노출이_승인된_토픽을_조회한다() {
// given
List<Topic> topics = List.of(
new Topic(dh, MUSIC, new ListTitle("1"), new ListDescription("1"), false, true),
Expand All @@ -144,20 +145,121 @@ class 토픽_조회 {
topicRepository.saveAll(topics);

// when
ExposedTopicFindResponse result = topicService.findAllExposed(5L, 5);
long cursorId = topics.get(8).getId();
ExposedTopicFindResponse result = topicService.findAllExposed(cursorId, 5);

// then
assertAll(
() -> assertThat(result.hasNext()).isFalse(),
() -> assertThat(result.hasNext()).isTrue(),
() -> {
List<TopicDto> topicDtos = result.topics();

assertThat(result.cursorId()).isEqualTo(topicDtos.get(topicDtos.size() - 1).id());
assertThat(result.cursorId()).isEqualTo(topics.get(3).getId());
assertThat(topicDtos).extracting("title")
.isEqualTo(List.of("4", "3", "2", "1"));
.isEqualTo(List.of("8", "7", "6", "5", "4"));
}
);

}
}

@Nested
class 관리자용_토픽_조회 {

@Test
void cursorId가_null이고_size가_10일_때_모든_토픽을_조회한다() {
// given
List<Topic> topics = List.of(
new Topic(dh, MUSIC, new ListTitle("1"), new ListDescription("1"), false, true), // O
new Topic(dh, MUSIC, new ListTitle("2"), new ListDescription("2"), false, false),
new Topic(dh, MUSIC, new ListTitle("3"), new ListDescription("3"), false, true), // O
new Topic(dh, MUSIC, new ListTitle("4"), new ListDescription("4"), false, false),
new Topic(dh, MUSIC, new ListTitle("5"), new ListDescription("5"), false, true), // O
new Topic(dh, MUSIC, new ListTitle("6"), new ListDescription("6"), false, false),
new Topic(dh, MUSIC, new ListTitle("7"), new ListDescription("7"), false, true), // O
new Topic(dh, MUSIC, new ListTitle("8"), new ListDescription("8"), false, false),
new Topic(dh, MUSIC, new ListTitle("9"), new ListDescription("9"), false, true), // O
new Topic(dh, MUSIC, new ListTitle("10"), new ListDescription("10"), false, false),
new Topic(dh, MUSIC, new ListTitle("11"), new ListDescription("11"), false, true), // O
new Topic(dh, MUSIC, new ListTitle("12"), new ListDescription("12"), false, false),
new Topic(dh, MUSIC, new ListTitle("13"), new ListDescription("13"), false, true) // O
);
topicRepository.saveAll(topics);

// when
TopicFindResponse result = topicService.findAll(null, 10);

// then
assertAll(
() -> assertThat(result.hasNext()).isTrue(),
() -> assertThat(result.totalCount()).isEqualTo(2),
() -> assertThat(result.topics()).extracting("title")
.isEqualTo(List.of("1", "2", "3", "4", "5", "6", "7", "8", "9", "10"))
);
}

@Test
void cursorId가_열_번째_ID이고_size가_10일_때_모든_토픽을_조회한다() {
// given
List<Topic> topics = List.of(
new Topic(dh, MUSIC, new ListTitle("1"), new ListDescription("1"), false, true), // O
new Topic(dh, MUSIC, new ListTitle("2"), new ListDescription("2"), false, false),
new Topic(dh, MUSIC, new ListTitle("3"), new ListDescription("3"), false, true), // O
new Topic(dh, MUSIC, new ListTitle("4"), new ListDescription("4"), false, false),
new Topic(dh, MUSIC, new ListTitle("5"), new ListDescription("5"), false, true), // O
new Topic(dh, MUSIC, new ListTitle("6"), new ListDescription("6"), false, false),
new Topic(dh, MUSIC, new ListTitle("7"), new ListDescription("7"), false, true), // O
new Topic(dh, MUSIC, new ListTitle("8"), new ListDescription("8"), false, false),
new Topic(dh, MUSIC, new ListTitle("9"), new ListDescription("9"), false, true), // O
new Topic(dh, MUSIC, new ListTitle("10"), new ListDescription("10"), false, false),
new Topic(dh, MUSIC, new ListTitle("11"), new ListDescription("11"), false, true), // O
new Topic(dh, MUSIC, new ListTitle("12"), new ListDescription("12"), false, false),
new Topic(dh, MUSIC, new ListTitle("13"), new ListDescription("13"), false, true) // O
);
topicRepository.saveAll(topics);

// when
TopicFindResponse result = topicService.findAll(topics.get(9).getId(), 10);

// then
assertAll(
() -> assertThat(result.hasNext()).isFalse(),
() -> assertThat(result.totalCount()).isEqualTo(2),
() -> assertThat(result.topics()).extracting("title")
.isEqualTo(List.of("11", "12", "13"))
);
}

@Test
void cursorId가_다섯_번째이고_size가_5일_때_모든_토픽을_조회한다() {
// given
List<Topic> topics = List.of(
new Topic(dh, MUSIC, new ListTitle("1"), new ListDescription("1"), false, true), // O
new Topic(dh, MUSIC, new ListTitle("2"), new ListDescription("2"), false, false),
new Topic(dh, MUSIC, new ListTitle("3"), new ListDescription("3"), false, true), // O
new Topic(dh, MUSIC, new ListTitle("4"), new ListDescription("4"), false, false),
new Topic(dh, MUSIC, new ListTitle("5"), new ListDescription("5"), false, true), // O
new Topic(dh, MUSIC, new ListTitle("6"), new ListDescription("6"), false, false),
new Topic(dh, MUSIC, new ListTitle("7"), new ListDescription("7"), false, true), // O
new Topic(dh, MUSIC, new ListTitle("8"), new ListDescription("8"), false, false),
new Topic(dh, MUSIC, new ListTitle("9"), new ListDescription("9"), false, true), // O
new Topic(dh, MUSIC, new ListTitle("10"), new ListDescription("10"), false, false),
new Topic(dh, MUSIC, new ListTitle("11"), new ListDescription("11"), false, true), // O
new Topic(dh, MUSIC, new ListTitle("12"), new ListDescription("12"), false, false),
new Topic(dh, MUSIC, new ListTitle("13"), new ListDescription("13"), false, true) // O
);
topicRepository.saveAll(topics);

// when
TopicFindResponse result = topicService.findAll(topics.get(4).getId(), 5);

// then
assertAll(
() -> assertThat(result.hasNext()).isTrue(),
() -> assertThat(result.totalCount()).isEqualTo(3),
() -> assertThat(result.topics()).extracting("title")
.isEqualTo(List.of("6", "7", "8", "9", "10"))
);
}
}
}

0 comments on commit 739763b

Please sign in to comment.