Skip to content

Commit

Permalink
Merge pull request #101 from oduck-team/feature/93
Browse files Browse the repository at this point in the history
짧은 리뷰 조회 수정 #93
  • Loading branch information
hanyMK authored Nov 15, 2023
2 parents e63befe + 159182b commit 1d1dea3
Show file tree
Hide file tree
Showing 15 changed files with 213 additions and 114 deletions.
30 changes: 27 additions & 3 deletions src/docs/asciidoc/index.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -740,6 +740,33 @@ include::{snippets}/postShortReview/success/http-response.adoc[]

==== 실패시

=== shortReview/getAttractionPoint api/v1/short-reviews//attraction-points

.curl-request
include::{snippets}/shortReview/getAttractionPoints/success/curl-request.adoc[]

.http-request
include::{snippets}/shortReview/getAttractionPoints/success/http-request.adoc[]

.httpie-request
include::{snippets}/shortReview/getAttractionPoints/success/httpie-request.adoc[]

.request-headers
include::{snippets}/shortReview/getAttractionPoints/success/request-headers.adoc[]

.response-body
include::{snippets}/shortReview/getAttractionPoints/success/response-body.adoc[]

.query-parameters
include::{snippets}/shortReview/getAttractionPoints/success/query-parameters.adoc[]

==== 성공시
.http-response
include::{snippets}/shortReview/getAttractionPoints/success/http-response.adoc[]

.response-fields
include::{snippets}/shortReview/getAttractionPoints/success/response-fields.adoc[]

=== PATCH api/v1/short-reviews/1

.curl-request
Expand All @@ -761,9 +788,6 @@ include::{snippets}/patchShortReview/success/request-fields.adoc[]
.http-response
include::{snippets}/patchShortReview/success/http-response.adoc[]

.response-body
include::{snippets}/patchShortReview/success/response-body.adoc[]

.response-fields
include::{snippets}/patchShortReview/success/response-fields.adoc[]

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,13 +37,12 @@ public class ShortReviewController {
@PostMapping
public ResponseEntity<?> postShortReview(
@LoginUser AuthUser user,
@RequestBody @Valid ShortReviewReqDto.PostShortReviewReq req) {
@RequestBody @Valid ShortReviewReqDto.ShortReviewReq req) {
//TODO : 짧은 리뷰 작성
shortReviewService.save(user.getId() == null? 0L: user.getId(), req);
return ResponseEntity.ok().build();
}

@GetMapping("/{animeId}")
@GetMapping("/animes/{animeId}")
public ResponseEntity<?> getShortReviews(
@PathVariable Long animeId,
@RequestParam(required = false) String cursor,
Expand All @@ -55,15 +54,23 @@ public ResponseEntity<?> getShortReviews(
SliceResponse<ShortReviewRes> reviewRes = shortReviewService.getShortReviews(animeId, cursor, sort, order, size);
return ResponseEntity.ok(reviewRes);
}
@PatchMapping("/{reviewId}")
@GetMapping("/attraction-points")
public ResponseEntity<?> patchShortReview(
@LoginUser AuthUser user,
@RequestParam(required = false) Long animeId,
@RequestParam(required = false) String name) {
//TODO : 짧은 리뷰 수정, 입덕포인트 반환
IsAttractionPoint attractionPointRes = attractionPointService.isAttractionPoint(user.getId(), animeId);
return ResponseEntity.ok(attractionPointRes);
}

@PatchMapping("/{reviewId}")
public ResponseEntity<?> patchShortReview(
@LoginUser AuthUser user,
@PathVariable Long reviewId,
@RequestBody @Valid ShortReviewReqDto.PatchShortReviewReq req) {
@RequestBody @Valid ShortReviewReqDto.ShortReviewReq req) {
//TODO : 짧은 리뷰 수정
shortReviewService.update(user.getId(), reviewId, req);
//입덕포인트 반환
IsAttractionPoint attractionPointRes = attractionPointService.isAttractionPoint(user.getId(), req.getAnimeId());
return ResponseEntity.ok(attractionPointRes);
return ResponseEntity.noContent().build();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,24 +10,26 @@ public class ShortReviewDslDto {
@Getter
@NoArgsConstructor
public static class ShortReviewDsl{
private Long reviewId;
private Long animeId;
private String name;
private String thumbnail;
private Integer score;
private String content;
private boolean hasSpoiler;
private Boolean isSpoiler;
private Long likeCount;
private LocalDateTime createdAt;

@Builder
public ShortReviewDsl(Long animeId, String name, String thumbnail, Integer score, String content,
boolean hasSpoiler, Long likeCount, LocalDateTime createdAt) {
public ShortReviewDsl(Long reviewId, Long animeId, String name, String thumbnail, Integer score, String content,
Boolean isSpoiler, Long likeCount, LocalDateTime createdAt) {
this.reviewId = reviewId;
this.animeId = animeId;
this.name = name;
this.thumbnail = thumbnail;
this.score = score;
this.content = content;
this.hasSpoiler = hasSpoiler;
this.isSpoiler = isSpoiler;
this.likeCount = likeCount;
this.createdAt = createdAt;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,21 +13,7 @@ public class ShortReviewReqDto {
@NoArgsConstructor
@AllArgsConstructor
@Builder
public static class PostShortReviewReq{
private Long animeId;
private String name;
private boolean hasSpoiler;
@NotBlank
@Length(min = 10, max = 100,
message = "최소 10에서 100자 까지 입력 가능합니다.")
private String content;
}

@Getter
@NoArgsConstructor
@AllArgsConstructor
@Builder
public static class PatchShortReviewReq{
public static class ShortReviewReq{
private Long animeId;
private String name;
private boolean hasSpoiler;
Expand All @@ -42,7 +28,7 @@ public static class PatchShortReviewReq{
public enum Sort{
//좋아요순(기본), 최신순, 평점 높은순, 평점 낮은순으로 조회 가능
CREATED_AT("createdAt"),
LIKE("likeCount"),
LIKE_COUNT("likeCount"),
SCORE("score");

private final String sort;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,31 +15,33 @@ public class ShortReviewResDto {;
@NoArgsConstructor(access = AccessLevel.PROTECTED)
@AllArgsConstructor
public static class ShortReviewRes implements EntityBased {
private Long reviewId;
private Long animeId;
private String name;
private String thumbnail;
private Integer score;
private String content;
private boolean hasSpoiler;
private boolean hasLike;
private Boolean isSpoiler;
private Boolean isLike;
@Builder.Default
private Long likeCount = 0L;
private LocalDateTime createdAt;

//카운트가 없으면 hasLike false
@Builder
public static ShortReviewRes of(ShortReviewDsl shortReviewDsl){
boolean hasLike = shortReviewDsl.getLikeCount() != null;
Long likeCount = hasLike ? shortReviewDsl.getLikeCount() : 0L ;
Boolean isLike = shortReviewDsl.getLikeCount() != null;
Long likeCount = isLike ? shortReviewDsl.getLikeCount() : 0L ;
return ShortReviewRes
.builder()
.reviewId(shortReviewDsl.getReviewId())
.animeId(shortReviewDsl.getAnimeId())
.name(shortReviewDsl.getName())
.thumbnail(shortReviewDsl.getThumbnail())
.score(shortReviewDsl.getScore())
.content(shortReviewDsl.getContent())
.hasSpoiler(shortReviewDsl.isHasSpoiler())
.hasLike(hasLike)
.isSpoiler(shortReviewDsl.getIsSpoiler())
.isLike(isLike)
.likeCount(likeCount)
.createdAt(shortReviewDsl.getCreatedAt())
.build();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import static io.oduck.api.domain.review.entity.QShortReview.shortReview;
import static io.oduck.api.domain.reviewLike.entity.QShortReviewLike.shortReviewLike;
import static io.oduck.api.domain.starRating.entity.QStarRating.starRating;

import static io.oduck.api.global.utils.QueryDslUtils.fetchSliceByCursor;

import com.querydsl.core.types.Path;
Expand All @@ -30,15 +31,17 @@
public class ShortReviewRepositoryImpl implements ShortReviewRepositoryCustom{

private final JPAQueryFactory query;


@Override
public Slice<ShortReviewDsl> selectShortReviews(Long animeId, String cursor,
Pageable pageable) {
String property = pageable.getSort().get().toList().get(0).getProperty();

JPAQuery<ShortReviewDsl> shortReviews = query
.select(
Projections.constructor(
ShortReviewDsl.class,
shortReview.id,
anime.id,
member.memberProfile.name,
member.memberProfile.thumbnail,
Expand All @@ -52,13 +55,14 @@ public Slice<ShortReviewDsl> selectShortReviews(Long animeId, String cursor,
.from(shortReview)
.join(member).on(member.id.eq(shortReview.member.id))
.join(anime).on(anime.id.eq(shortReview.anime.id))
.join(shortReviewLike).on(shortReviewLike.shortReview.id.eq(shortReview.id))
.leftJoin(starRating).on(starRating.anime.id.eq(anime.id).and(starRating.member.id.eq(member.id)))
.leftJoin(shortReviewLike).on(shortReview.id.eq(shortReviewLike.shortReview.id))
.join(starRating).on(starRating.anime.id.eq(shortReview.anime.id).and(starRating.member.id.eq(shortReview.member.id)))
.where(anime.id.eq(animeId))
.where(cursorCondition(cursor, pageable))
.groupBy(shortReview.id, member.id)
.having(cursorCondition(cursor, pageable))
.limit(pageable.getPageSize());

return fetchSliceByCursor(sortPath(property),shortReviews, pageable);
return fetchSliceByCursor(sortPath(property), shortReviews, pageable );
}

private BooleanExpression cursorCondition(String cursor, Pageable pageable){
Expand All @@ -69,6 +73,7 @@ private BooleanExpression cursorCondition(String cursor, Pageable pageable){
String property = orderList.get(0).getProperty();

switch (property){

case "likeCount":
String[] likeCountAndCreateAt = cursor.split(", ");
int likeCount = Integer.parseInt(likeCountAndCreateAt[0]);
Expand All @@ -77,11 +82,11 @@ private BooleanExpression cursorCondition(String cursor, Pageable pageable){
if(direction == Direction.ASC) {
return shortReviewLike.id.count().gt(likeCount)
.or(shortReviewLike.id.count().goe(likeCount).and(shortReview.createdAt.lt(likeCountCreateAt)))//조회할 좋아요가 크거나 같으면, 첫 커서의 날짜가 크면
.or(shortReviewLike.id.count().isNotNull().and(shortReview.createdAt.lt(likeCountCreateAt)));
.or(shortReviewLike.id.count().isNull().and(shortReview.createdAt.lt(likeCountCreateAt)));
} else{
return shortReviewLike.id.count().lt(likeCount)
.or(shortReviewLike.id.count().loe(likeCount).and(shortReview.createdAt.lt(likeCountCreateAt)))//조회할 좋아요가 크거나 같으면, 첫 커서의 날짜가 크면
.or(shortReviewLike.id.count().isNotNull().and(shortReview.createdAt.lt(likeCountCreateAt)));
.or(shortReviewLike.id.count().isNull().and(shortReview.createdAt.lt(likeCountCreateAt)));
}

case "score":
Expand All @@ -91,12 +96,10 @@ private BooleanExpression cursorCondition(String cursor, Pageable pageable){

if(direction == Direction.ASC) {
return starRating.score.gt(score)
.or(starRating.score.goe(score).and(shortReview.createdAt.lt(scoreCreateAt)))//조회할 좋아요가 크거나 같으면, 첫 커서의 날짜가 크면
.or(starRating.score.isNotNull().and(shortReview.createdAt.lt(scoreCreateAt)));
.or(starRating.score.goe(score).and(shortReview.createdAt.lt(scoreCreateAt)));
} else{
return shortReviewLike.id.count().lt(score)
.or(shortReviewLike.id.count().loe(score).and(shortReview.createdAt.lt(scoreCreateAt)))//조회할 좋아요가 크거나 같으면, 첫 커서의 날짜가 크면
.or(shortReviewLike.id.count().isNotNull().and(shortReview.createdAt.lt(scoreCreateAt)));
return starRating.score.lt(score)
.or(starRating.score.loe(score).and(shortReview.createdAt.lt(scoreCreateAt)));
}
default:
if (direction == Direction.ASC) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
package io.oduck.api.domain.review.service;

import io.oduck.api.domain.review.dto.ShortReviewReqDto.PatchShortReviewReq;
import io.oduck.api.domain.review.dto.ShortReviewReqDto.PostShortReviewReq;
import io.oduck.api.domain.review.dto.ShortReviewReqDto.ShortReviewReq;
import io.oduck.api.domain.review.dto.ShortReviewReqDto.Sort;
import io.oduck.api.domain.review.dto.ShortReviewResDto.ShortReviewCountRes;
import io.oduck.api.domain.review.dto.ShortReviewResDto.ShortReviewRes;
Expand All @@ -11,14 +10,14 @@
public interface ShortReviewService {

//애니 리뷰 작성
void save(Long memberId, PostShortReviewReq shortReviewReq);
void save(Long memberId, ShortReviewReq shortReviewReq);

//애니 짧은 리뷰 조회
SliceResponse<ShortReviewRes> getShortReviews(Long animeId, String cursor, Sort sort, OrderDirection order, int size);
ShortReviewCountRes getShortReviewCountByMemberId(Long memberId);

//애니 리뷰 수정
void update(Long memberId, Long reviewId, PatchShortReviewReq req);
void update(Long memberId, Long reviewId, ShortReviewReq req);

//애니 리뷰 삭제

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,11 @@
import io.oduck.api.domain.anime.entity.Anime;
import io.oduck.api.domain.anime.repository.AnimeRepository;
import io.oduck.api.domain.member.entity.Member;
import io.oduck.api.domain.member.repository.MemberProfileRepository;
import io.oduck.api.domain.member.repository.MemberRepository;
import io.oduck.api.domain.review.dto.ShortReviewDslDto.ShortReviewDsl;
import io.oduck.api.domain.review.dto.ShortReviewReqDto;
import io.oduck.api.domain.review.dto.ShortReviewReqDto.PatchShortReviewReq;
import io.oduck.api.domain.review.dto.ShortReviewReqDto.PostShortReviewReq;
import io.oduck.api.domain.review.dto.ShortReviewReqDto.ShortReviewReq;

import io.oduck.api.domain.review.dto.ShortReviewResDto.ShortReviewCountRes;
import io.oduck.api.domain.review.dto.ShortReviewResDto.ShortReviewRes;
import io.oduck.api.domain.review.entity.ShortReview;
Expand All @@ -35,15 +34,13 @@
public class ShortReviewServiceImpl implements ShortReviewService{

private final ShortReviewRepository shortReviewRepository;
private final MemberProfileRepository memberProfileRepository;
private final MemberRepository memberRepository;

private final AnimeRepository animeRepository;


@Override
@Transactional
public void save(Long memberId, PostShortReviewReq shortReviewReq) {
public void save(Long memberId, ShortReviewReq shortReviewReq) {
ShortReview shortReview = ShortReview
.builder()
.content(shortReviewReq.getContent())
Expand All @@ -60,7 +57,7 @@ public void save(Long memberId, PostShortReviewReq shortReviewReq) {
//회원 입력
Member member = memberRepository.findById(memberId)
.orElseThrow(
() -> new NotFoundException("Memebr")
() -> new NotFoundException("Member")
);
shortReview.relateMember(member);

Expand All @@ -77,7 +74,7 @@ public SliceResponse<ShortReviewRes> getShortReviews(Long animeId, String cursor
sort.getSort()
);

if(sort == ShortReviewReqDto.Sort.LIKE){
if(sort == ShortReviewReqDto.Sort.LIKE_COUNT){
sortList = sortList.and(Sort.by(Direction.DESC, "createdAt"));
}else if(sort == ShortReviewReqDto.Sort.SCORE){
sortList = sortList.and(Sort.by(Direction.DESC, "createdAt"));
Expand Down Expand Up @@ -107,9 +104,8 @@ public ShortReviewCountRes getShortReviewCountByMemberId(Long memberId) {
.count(count)
.build();
}

@Override
public void update(Long memberId, Long reviewId, PatchShortReviewReq req) {
public void update(Long memberId, Long reviewId, ShortReviewReq req) {
ShortReview findShortReview = getShortReview(reviewId);
Long findMemberId = findShortReview.getMember().getId();
//리뷰 작성자 인지 확인
Expand Down
7 changes: 7 additions & 0 deletions src/main/java/io/oduck/api/global/utils/QueryDslUtils.java
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import com.querydsl.core.types.OrderSpecifier;
import com.querydsl.core.types.Path;
import com.querydsl.core.types.dsl.Expressions;
import com.querydsl.core.types.dsl.NumberPath;
import com.querydsl.jpa.impl.JPAQuery;
import java.util.ArrayList;
import java.util.Iterator;
Expand Down Expand Up @@ -66,6 +67,12 @@ private static List<OrderSpecifier> convertToDslOrder(Sort sort, List<Path> path
}

private static OrderSpecifier<?> createOrderSpecifier(Order order, Path<?> parent, String fieldName) {
// count 메소드 컬럼을 기준으로 할 때의 OrderSpecifier
if (fieldName.equals("quantity") || fieldName.equals("likeCount")) {
NumberPath<Long> aliasQuantity = Expressions.numberPath(Long.class, fieldName);

return new OrderSpecifier(order, aliasQuantity);
}
// 일반 컬럼을 기준으로 할때의 OrderSpecifier
Path<Object> fieldPath = Expressions.path(Object.class, parent, fieldName);

Expand Down
1 change: 1 addition & 0 deletions src/main/resources/application-test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ spring:
password:
sql:
init:
encoding: utf-8
mode: always
data-locations: classpath:db/data.sql
security:
Expand Down
Loading

0 comments on commit 1d1dea3

Please sign in to comment.