Skip to content

Commit

Permalink
[feat/CK-114] 로드맵 리뷰를 조회하는 기능을 구현한다 (#74)
Browse files Browse the repository at this point in the history
* feat: 로드맵 리뷰 조회 API 구현

* feat: 로드맵 리뷰 조회 Repository 구현

* feat: 로드맵 리뷰 조회 Service 구현

* feat: 로드맵 리뷰 조회 무한 스크롤 방식으로 변경 및 통합 테스트 추가

* refactor: 리뷰 반영

* refactor: LocalDateTime 요청/응답 formatting문제 해결

* test: 사용자 프로필 이미지 경로도 확인하도록 수정
  • Loading branch information
Ohjintaek authored Aug 10, 2023
1 parent 84374ae commit 024aebc
Show file tree
Hide file tree
Showing 19 changed files with 821 additions and 228 deletions.
11 changes: 11 additions & 0 deletions backend/kirikiri/src/docs/asciidoc/roadmap.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -193,3 +193,14 @@ operation::roadmap-read-api-test/로드맵의_골룸_목록을_조건에_따라_
=== *8-2* 실패 - 존재하지 않는 로드맵인 경우

operation::roadmap-read-api-test/로드맵의_골룸_목록을_조건에_따라_조회할_때_로드맵이_존재하지_않으면_예외가_발생한다[snippets='http-request,query-parameters,http-response,response-fields']

[[로드맵리뷰조회-API]]
== *9. 로드맵의 리뷰 목록 조회 API*

=== *9-1* 성공

operation::roadmap-read-api-test/로드맵의_리뷰들을_조회한다[snippets='http-request,http-response,path-parameters,query-parameters,response-fields']

=== *9-1* 실패 - 존재하지 않는 로드맵인 경우

operation::roadmap-read-api-test/로드맵_리뷰_조회_시_유효하지_않은_로드맵_아이디일_경우_예외를_반환한다[snippets='http-request,http-response,path-parameters,query-parameters,response-fields']
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import co.kirikiri.common.resolver.MemberIdentifier;
import co.kirikiri.service.RoadmapCreateService;
import co.kirikiri.service.RoadmapReadService;
import co.kirikiri.service.dto.CustomReviewScrollRequest;
import co.kirikiri.service.dto.CustomScrollRequest;
import co.kirikiri.service.dto.roadmap.RoadmapGoalRoomsFilterTypeDto;
import co.kirikiri.service.dto.roadmap.request.RoadmapFilterTypeRequest;
Expand All @@ -15,6 +16,7 @@
import co.kirikiri.service.dto.roadmap.response.RoadmapForListResponses;
import co.kirikiri.service.dto.roadmap.response.RoadmapGoalRoomResponses;
import co.kirikiri.service.dto.roadmap.response.RoadmapResponse;
import co.kirikiri.service.dto.roadmap.response.RoadmapReviewResponse;
import jakarta.validation.Valid;
import java.net.URI;
import java.util.List;
Expand Down Expand Up @@ -107,4 +109,14 @@ public ResponseEntity<RoadmapGoalRoomResponses> findGoalRoomsByFilterType(
roadmapId, roadmapGoalRoomsFilterTypeDto, scrollRequest);
return ResponseEntity.ok(responses);
}

@GetMapping("/{roadmapId}/reviews")
public ResponseEntity<List<RoadmapReviewResponse>> findRoadmapReviews(
@PathVariable final Long roadmapId,
@ModelAttribute final CustomReviewScrollRequest reviewScrollRequest
) {
final List<RoadmapReviewResponse> responses = roadmapReadService.findRoadmapReviews(roadmapId,
reviewScrollRequest);
return ResponseEntity.ok(responses);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
import jakarta.persistence.FetchType;
import jakarta.persistence.JoinColumn;
import jakarta.persistence.ManyToOne;
import java.time.LocalDateTime;
import java.util.regex.Pattern;
import lombok.AccessLevel;
import lombok.NoArgsConstructor;
Expand Down Expand Up @@ -73,4 +74,20 @@ public void updateRoadmap(final Roadmap roadmap) {
public boolean isNotSameRoadmap(final Roadmap roadmap) {
return this.roadmap == null || !this.roadmap.equals(roadmap);
}

public String getContent() {
return content;
}

public Double getRate() {
return rate;
}

public Member getMember() {
return member;
}

public LocalDateTime getCreatedAt() {
return createdAt;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package co.kirikiri.persistence.dto;

import co.kirikiri.service.dto.CustomReviewScrollRequest;
import java.time.LocalDateTime;
import lombok.AccessLevel;
import lombok.AllArgsConstructor;
import lombok.Getter;

@AllArgsConstructor(access = AccessLevel.PRIVATE)
@Getter
public class RoadmapReviewLastValueDto {

private final LocalDateTime lastCreatedAt;
private final Double lastReviewRate;

public static RoadmapReviewLastValueDto create(final CustomReviewScrollRequest request) {
if (request.lastCreatedAt() == null && request.lastReviewRate() == null) {
return null;
}
if (request.lastReviewRate() == null) {
return new RoadmapReviewLastValueDto(request.lastCreatedAt(), null);
}
return new RoadmapReviewLastValueDto(null, request.lastReviewRate());
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package co.kirikiri.persistence.roadmap;

import co.kirikiri.domain.roadmap.Roadmap;
import co.kirikiri.domain.roadmap.RoadmapReview;
import co.kirikiri.persistence.dto.RoadmapReviewLastValueDto;
import java.util.List;

public interface RoadmapReviewQueryRepository {

List<RoadmapReview> findRoadmapReviewWithMemberByRoadmapOrderByLatest(final Roadmap roadmap,
final RoadmapReviewLastValueDto lastValue,
final int pageSize);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
package co.kirikiri.persistence.roadmap;

import static co.kirikiri.domain.member.QMember.member;
import static co.kirikiri.domain.roadmap.QRoadmapReview.roadmapReview;

import co.kirikiri.domain.roadmap.Roadmap;
import co.kirikiri.domain.roadmap.RoadmapReview;
import co.kirikiri.persistence.QuerydslRepositorySupporter;
import co.kirikiri.persistence.dto.RoadmapReviewLastValueDto;
import com.querydsl.core.types.OrderSpecifier;
import com.querydsl.core.types.dsl.BooleanExpression;
import java.time.LocalDateTime;
import java.util.List;

public class RoadmapReviewQueryRepositoryImpl extends QuerydslRepositorySupporter implements
RoadmapReviewQueryRepository {

public RoadmapReviewQueryRepositoryImpl() {
super(RoadmapReview.class);
}

@Override
public List<RoadmapReview> findRoadmapReviewWithMemberByRoadmapOrderByLatest(final Roadmap roadmap,
final RoadmapReviewLastValueDto lastValue,
final int pageSize) {
return selectFrom(roadmapReview)
.innerJoin(roadmapReview.member, member)
.fetchJoin()
.where(roadmapCond(roadmap), lessThanLastValue(lastValue))
.limit(pageSize)
.orderBy(orderByCreatedAtDesc())
.fetch();
}

private BooleanExpression roadmapCond(final Roadmap roadmap) {
return roadmapReview.roadmap.eq(roadmap);
}

private BooleanExpression lessThanLastValue(final RoadmapReviewLastValueDto lastValue) {
if (lastValue == null) {
return null;
}
return roadmapReview.createdAt.lt(lastValue.getLastCreatedAt());
}

private OrderSpecifier<LocalDateTime> orderByCreatedAtDesc() {
return roadmapReview.createdAt.desc();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
import java.util.Optional;
import org.springframework.data.jpa.repository.JpaRepository;

public interface RoadmapReviewRepository extends JpaRepository<RoadmapReview, Long> {
public interface RoadmapReviewRepository extends JpaRepository<RoadmapReview, Long>, RoadmapReviewQueryRepository {

Optional<RoadmapReview> findByRoadmapAndMember(final Roadmap roadmap, final Member member);
}
Original file line number Diff line number Diff line change
@@ -1,23 +1,26 @@
package co.kirikiri.service;

import co.kirikiri.domain.goalroom.GoalRoom;
import co.kirikiri.domain.goalroom.GoalRoomStatus;
import co.kirikiri.domain.member.Member;
import co.kirikiri.domain.member.vo.Identifier;
import co.kirikiri.domain.roadmap.Roadmap;
import co.kirikiri.domain.roadmap.RoadmapCategory;
import co.kirikiri.domain.roadmap.RoadmapContent;
import co.kirikiri.domain.roadmap.RoadmapReview;
import co.kirikiri.exception.NotFoundException;
import co.kirikiri.persistence.dto.GoalRoomLastValueDto;
import co.kirikiri.persistence.dto.RoadmapFilterType;
import co.kirikiri.persistence.dto.RoadmapLastValueDto;
import co.kirikiri.persistence.dto.RoadmapReviewLastValueDto;
import co.kirikiri.persistence.dto.RoadmapSearchDto;
import co.kirikiri.persistence.goalroom.GoalRoomRepository;
import co.kirikiri.persistence.goalroom.dto.RoadmapGoalRoomsFilterType;
import co.kirikiri.persistence.member.MemberRepository;
import co.kirikiri.persistence.roadmap.RoadmapCategoryRepository;
import co.kirikiri.persistence.roadmap.RoadmapContentRepository;
import co.kirikiri.persistence.roadmap.RoadmapRepository;
import co.kirikiri.persistence.roadmap.RoadmapReviewRepository;
import co.kirikiri.service.dto.CustomReviewScrollRequest;
import co.kirikiri.service.dto.CustomScrollRequest;
import co.kirikiri.service.dto.roadmap.RoadmapGoalRoomNumberDto;
import co.kirikiri.service.dto.roadmap.RoadmapGoalRoomsFilterTypeDto;
Expand All @@ -28,12 +31,10 @@
import co.kirikiri.service.dto.roadmap.response.RoadmapForListResponses;
import co.kirikiri.service.dto.roadmap.response.RoadmapGoalRoomResponses;
import co.kirikiri.service.dto.roadmap.response.RoadmapResponse;
import co.kirikiri.service.dto.roadmap.response.RoadmapReviewResponse;
import co.kirikiri.service.mapper.GoalRoomMapper;
import co.kirikiri.service.mapper.RoadmapMapper;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
Expand All @@ -46,6 +47,7 @@ public class RoadmapReadService {
private final RoadmapRepository roadmapRepository;
private final RoadmapCategoryRepository roadmapCategoryRepository;
private final RoadmapContentRepository roadmapContentRepository;
private final RoadmapReviewRepository roadmapReviewRepository;
private final GoalRoomRepository goalRoomRepository;
private final MemberRepository memberRepository;

Expand Down Expand Up @@ -127,4 +129,14 @@ public RoadmapGoalRoomResponses findRoadmapGoalRoomsByFilterType(final Long road
roadmap, filterType, goalRoomLastValueDto, scrollRequest.size());
return GoalRoomMapper.convertToRoadmapGoalRoomResponses(goalRoomsWithPendingMembers, scrollRequest.size());
}

public List<RoadmapReviewResponse> findRoadmapReviews(final Long roadmapId,
final CustomReviewScrollRequest reviewScrollRequest) {
final Roadmap roadmap = findRoadmapById(roadmapId);
final RoadmapReviewLastValueDto roadmapReviewLastValueDto = RoadmapReviewLastValueDto.create(
reviewScrollRequest);
final List<RoadmapReview> roadmapReviews = roadmapReviewRepository.findRoadmapReviewWithMemberByRoadmapOrderByLatest(
roadmap, roadmapReviewLastValueDto, reviewScrollRequest.size());
return RoadmapMapper.convertToRoadmapReviewResponses(roadmapReviews);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package co.kirikiri.service.dto;

import jakarta.validation.constraints.NotNull;
import java.time.LocalDateTime;
import org.springframework.format.annotation.DateTimeFormat;

public record CustomReviewScrollRequest(
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss.SSSSSS")
LocalDateTime lastCreatedAt,
Double lastReviewRate,
@NotNull(message = "사이즈를 입력해 주세요.")
Integer size
) {

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package co.kirikiri.service.dto.roadmap.response;

import co.kirikiri.service.dto.member.response.MemberResponse;
import java.time.LocalDateTime;

public record RoadmapReviewResponse(
Long id,
MemberResponse member,
LocalDateTime createdAt,
String content,
Double rate
) {

}
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
import co.kirikiri.domain.roadmap.RoadmapNode;
import co.kirikiri.domain.roadmap.RoadmapNodeImage;
import co.kirikiri.domain.roadmap.RoadmapNodes;
import co.kirikiri.domain.roadmap.RoadmapReview;
import co.kirikiri.domain.roadmap.RoadmapTags;
import co.kirikiri.persistence.dto.RoadmapFilterType;
import co.kirikiri.service.dto.member.response.MemberResponse;
Expand All @@ -28,6 +29,7 @@
import co.kirikiri.service.dto.roadmap.response.RoadmapForListResponses;
import co.kirikiri.service.dto.roadmap.response.RoadmapNodeResponse;
import co.kirikiri.service.dto.roadmap.response.RoadmapResponse;
import co.kirikiri.service.dto.roadmap.response.RoadmapReviewResponse;
import co.kirikiri.service.dto.roadmap.response.RoadmapTagResponse;
import java.util.List;
import lombok.AccessLevel;
Expand Down Expand Up @@ -174,4 +176,19 @@ private static MemberRoadmapResponse convertMemberRoadmapResponse(final Roadmap
roadmap.getDifficulty().name(), roadmap.getCreatedAt(),
new RoadmapCategoryResponse(category.getId(), category.getName()));
}

public static List<RoadmapReviewResponse> convertToRoadmapReviewResponses(
final List<RoadmapReview> roadmapReviews) {
return roadmapReviews.stream()
.map(RoadmapMapper::convertToRoadmapReviewResponse)
.toList();
}

private static RoadmapReviewResponse convertToRoadmapReviewResponse(final RoadmapReview review) {
final Member member = review.getMember();
return new RoadmapReviewResponse(review.getId(),
new MemberResponse(member.getId(), member.getNickname().getValue(),
member.getImage().getServerFilePath()),
review.getCreatedAt(), review.getContent(), review.getRate());
}
}
2 changes: 1 addition & 1 deletion backend/kirikiri/src/main/resources/properties
Original file line number Diff line number Diff line change
Expand Up @@ -325,7 +325,7 @@ class GoalRoomReadApiTest extends ControllerTestHelper {
fieldWithPath("[0].endDate").description("골룸 종료 날짜"),
fieldWithPath("[0].goalRoomLeader.id").description("골룸 생성 사용자 아이디"),
fieldWithPath("[0].goalRoomLeader.name").description("골룸 생성 사용자 닉네임"),
fieldWithPath("[0].goalRoomLeader.imageUrl").description("골룸 생성 사용자 프포필 이미지 경로")
fieldWithPath("[0].goalRoomLeader.imageUrl").description("골룸 생성 사용자 프로필 이미지 경로")
)))
.andReturn().getResponse()
.getContentAsString();
Expand Down
Loading

0 comments on commit 024aebc

Please sign in to comment.