Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Feat/184] 리뷰 등록 API 구현 #196

Merged
merged 2 commits into from
Jun 26, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,9 @@ public enum ErrorStatus implements BaseErrorCode {
CART_DETAIL_NOT_FOUND(HttpStatus.NOT_FOUND, "CART4004", "해당 장바구니 상세 내역을 찾을 수 없습니다"),
DELETE_CART_DETAIL_FAILED(HttpStatus.BAD_REQUEST, "CART4005", "장바구니 상세 내역을 삭제할 수 없습니다."),

// 리뷰 관련 에러
REVIEW_ALREADY_EXISTS(HttpStatus.BAD_REQUEST, "REVIEW4001", "이미 리뷰를 작성한 주문내역 입니다."),

// test
TEMP_EXCEPTION(HttpStatus.BAD_REQUEST, "TEMP4001", "테스트"),

Expand All @@ -59,7 +62,7 @@ public enum ErrorStatus implements BaseErrorCode {
MEMBER_ADDRESS_NOT_FOUND(HttpStatus.NOT_FOUND, "MEMBER4009", "주소가 존재하지 않습니다"),
MEMBER_INACTIVATE(HttpStatus.NOT_ACCEPTABLE, "MEMBER4010", "탈퇴한 회원입니다."),
MEMBER_NOT_OWNER(HttpStatus.NOT_ACCEPTABLE, "MEMBER4011", "해당 회원이 아닙니다."),
MEMBER_CONTACT_NOT_FOUND(HttpStatus.NOT_FOUND,"MEMBER4012", "연락 가능 시간을 조회할 수 없습니다."),
MEMBER_CONTACT_NOT_FOUND(HttpStatus.NOT_FOUND, "MEMBER4012", "연락 가능 시간을 조회할 수 없습니다."),
MEMBER_EMAIL_DUPLICATED(HttpStatus.BAD_REQUEST, "MEMBER4013", "중복된 이메일입니다."),
MEMBER_EMAIL_INCORRECT(HttpStatus.BAD_REQUEST, "MEMBER4014", "잘못된 이메일입니다."),

Expand All @@ -77,7 +80,7 @@ public enum ErrorStatus implements BaseErrorCode {
POST_ALREADY_FOLLOW(HttpStatus.BAD_REQUEST, "POST4002", "이미 팔로우 했습니다."),
POST_FOLLOW_NOT_FOUND(HttpStatus.BAD_REQUEST, "POST4003", "해당 팔로우를 찾을수 없습니다."),
POST_NOT_FOUND(HttpStatus.BAD_REQUEST, "POST4004", "포스트를 찾을수 없습니다."),
POST_COMMENT_NOT_FOUND(HttpStatus.BAD_REQUEST,"POST4005", "해당 댓글을 찾을 수 없습니다."),
POST_COMMENT_NOT_FOUND(HttpStatus.BAD_REQUEST, "POST4005", "해당 댓글을 찾을 수 없습니다."),
POST_COMMENT_NOT_UPDATE(HttpStatus.BAD_REQUEST, "POST4006", "해당 댓글을 수정할 수 없습니다."),


Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package com.umc.TheGoods.apiPayload.exception.handler;

import com.umc.TheGoods.apiPayload.code.BaseErrorCode;
import com.umc.TheGoods.apiPayload.exception.GeneralException;

public class ReviewHandler extends GeneralException {
public ReviewHandler(BaseErrorCode code) {
super(code);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
package com.umc.TheGoods.converter.review;

import com.umc.TheGoods.domain.enums.ReviewStatus;
import com.umc.TheGoods.domain.item.Review;
import com.umc.TheGoods.domain.order.OrderItem;
import com.umc.TheGoods.web.dto.review.ReviewResponseDTO;

import java.util.List;
import java.util.stream.Collectors;

public class ReviewConverter {

public static Review toReview(OrderItem orderItem, String text, Integer score) {
return Review.builder()
.text(text)
.score(score)
.orderItem(orderItem)
.status(ReviewStatus.SHOW)
.build();
}

public static ReviewResponseDTO.reviewPostDTO toReviewPostDTO(Review review) {
return ReviewResponseDTO.reviewPostDTO.builder()
.reviewId(review.getId())
.createdAt(review.getCreatedAt())
.itemName(review.getItem().getName())
.score(review.getScore())
.text(review.getText())
.optionStringList(ReviewConverter.toOptionStringList(review.getOrderItem()))
.build();
}

private static List<String> toOptionStringList(OrderItem orderItem) {
return orderItem.getOrderDetailList().stream().map(orderDetail -> {
return orderDetail.getItemOption().getName();
}).collect(Collectors.toList());
}
}
22 changes: 19 additions & 3 deletions src/main/java/com/umc/TheGoods/domain/item/Review.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@

import com.umc.TheGoods.domain.common.BaseDateTimeEntity;
import com.umc.TheGoods.domain.enums.ReviewStatus;
import com.umc.TheGoods.domain.images.ReviewImg;
import com.umc.TheGoods.domain.member.Member;
import com.umc.TheGoods.domain.order.OrderItem;
import lombok.*;
Expand Down Expand Up @@ -44,8 +43,25 @@ public class Review extends BaseDateTimeEntity {
@OneToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "order_item_id", nullable = false)
private OrderItem orderItem;
//
// @OneToOne(mappedBy = "review", cascade = CascadeType.ALL)
// private ReviewImg reviewImg;

@OneToOne(mappedBy = "review", cascade = CascadeType.ALL)
private ReviewImg reviewImg;
// 연관관계 메소드
public void setMember(Member member) {
if (this.member != null) {
this.member.getReviewList().remove(this);
}
this.member = member;
this.member.getReviewList().add(this);
}

public void setItem(Item item) {
if (this.item != null) {
this.item.getReviewList().remove(this);
}
this.item = item;
this.item.getReviewList().add(this);
}

}
5 changes: 2 additions & 3 deletions src/main/java/com/umc/TheGoods/domain/order/OrderItem.java
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
import com.umc.TheGoods.domain.common.BaseDateTimeEntity;
import com.umc.TheGoods.domain.enums.OrderStatus;
import com.umc.TheGoods.domain.item.Item;
import com.umc.TheGoods.domain.item.Review;
import com.umc.TheGoods.domain.types.DeliveryType;
import com.umc.TheGoods.web.dto.order.OrderRequestDTO;
import lombok.*;
Expand Down Expand Up @@ -87,8 +86,8 @@ public class OrderItem extends BaseDateTimeEntity {
@OneToOne(mappedBy = "orderItem", cascade = CascadeType.ALL)
private OrderCancel orderCancel;

@OneToOne(mappedBy = "orderItem", cascade = CascadeType.ALL)
private Review review;
// @OneToOne(mappedBy = "orderItem", cascade = CascadeType.ALL)
// private Review review;

public void setOrders(Orders orders) {
if (this.orders != null) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package com.umc.TheGoods.repository.review;

import com.umc.TheGoods.domain.item.Review;
import com.umc.TheGoods.domain.order.OrderItem;
import org.springframework.data.jpa.repository.JpaRepository;

public interface ReviewRepository extends JpaRepository<Review, Long> {

boolean existsByOrderItem(OrderItem orderItem);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package com.umc.TheGoods.service.ReviewService;

import com.umc.TheGoods.domain.item.Review;
import com.umc.TheGoods.domain.member.Member;
import com.umc.TheGoods.web.dto.review.ReviewRequestDTO;

public interface ReviewCommandService {

Review create(ReviewRequestDTO.addReviewDTO request, Member member);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
package com.umc.TheGoods.service.ReviewService;

import com.umc.TheGoods.apiPayload.code.status.ErrorStatus;
import com.umc.TheGoods.apiPayload.exception.handler.OrderHandler;
import com.umc.TheGoods.apiPayload.exception.handler.ReviewHandler;
import com.umc.TheGoods.converter.review.ReviewConverter;
import com.umc.TheGoods.domain.item.Review;
import com.umc.TheGoods.domain.member.Member;
import com.umc.TheGoods.domain.order.OrderItem;
import com.umc.TheGoods.repository.order.OrderItemRepository;
import com.umc.TheGoods.repository.review.ReviewRepository;
import com.umc.TheGoods.web.dto.review.ReviewRequestDTO;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

@Slf4j
@Service
@RequiredArgsConstructor
@Transactional
public class ReviewCommandServiceImpl implements ReviewCommandService {

private final OrderItemRepository orderItemRepository;
private final ReviewRepository reviewRepository;

/**
* 리뷰 등록
*
* @param request
* @param member
* @return
*/
@Override
public Review create(ReviewRequestDTO.addReviewDTO request, Member member) {
// 해당 orderItem이 해당 회원의 것이 맞는지 검증
OrderItem orderItem = orderItemRepository.findById(request.getOrderItemId()).orElseThrow(() -> new OrderHandler(ErrorStatus.ORDER_ITEM_NOT_FOUND));
if (!orderItem.getOrders().getMember().equals(member)) {
throw new ReviewHandler(ErrorStatus.NOT_ORDER_OWNER);
}

// 이미 리뷰를 등록했던 orderItem인지 검증
boolean isExists = reviewRepository.existsByOrderItem(orderItem);
if (isExists) {
throw new ReviewHandler(ErrorStatus.REVIEW_ALREADY_EXISTS);
}

// review 엔티티 생성 및 연관관계 매핑
Review review = ReviewConverter.toReview(orderItem, request.getText(), request.getScore());
review.setMember(member);
review.setItem(orderItem.getItem());

return reviewRepository.save(review);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
package com.umc.TheGoods.web.controller;

import com.umc.TheGoods.apiPayload.ApiResponse;
import com.umc.TheGoods.apiPayload.code.status.ErrorStatus;
import com.umc.TheGoods.apiPayload.exception.handler.MemberHandler;
import com.umc.TheGoods.converter.review.ReviewConverter;
import com.umc.TheGoods.domain.item.Review;
import com.umc.TheGoods.domain.member.Member;
import com.umc.TheGoods.service.MemberService.MemberQueryService;
import com.umc.TheGoods.service.ReviewService.ReviewCommandService;
import com.umc.TheGoods.web.dto.review.ReviewRequestDTO;
import com.umc.TheGoods.web.dto.review.ReviewResponseDTO;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.security.core.Authentication;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.validation.Valid;

@Slf4j
@Tag(name = "Review", description = "리뷰 관련 API")
@Validated
@RestController
@RequiredArgsConstructor
@RequestMapping("/api/review")
public class ReviewController {
private final MemberQueryService memberQueryService;
private final ReviewCommandService reviewCommandService;

@PostMapping
public ApiResponse<ReviewResponseDTO.reviewPostDTO> addReview(@RequestBody @Valid ReviewRequestDTO.addReviewDTO request,
Authentication authentication) {
// 비회원인 경우 처리 불가
if (authentication == null) {
throw new MemberHandler(ErrorStatus._UNAUTHORIZED);
}

// request에서 member id 추출해 Member 엔티티 찾기
Member member = memberQueryService.findMemberById(Long.valueOf(authentication.getName().toString())).orElseThrow(() -> new MemberHandler(ErrorStatus.MEMBER_NOT_FOUND));

// 리뷰 등록
Review review = reviewCommandService.create(request, member);

return ApiResponse.onSuccess(ReviewConverter.toReviewPostDTO(review));
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package com.umc.TheGoods.web.dto.review;

import lombok.Getter;

import javax.validation.constraints.Max;
import javax.validation.constraints.Min;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Size;

public class ReviewRequestDTO {

@Getter
public static class addReviewDTO {
@NotNull
@Min(1)
@Max(5)
Integer score;

@NotNull
Long orderItemId;

@Size(max = 1500)
String text;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package com.umc.TheGoods.web.dto.review;

import com.fasterxml.jackson.annotation.JsonInclude;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;

import java.time.LocalDateTime;
import java.util.List;

public class ReviewResponseDTO {
@Builder
@Getter
@NoArgsConstructor
@AllArgsConstructor
public static class reviewPostDTO {
Long reviewId;
LocalDateTime createdAt;
String itemName;
@JsonInclude(JsonInclude.Include.NON_NULL)
List<String> optionStringList;
Integer score;
String text;
}
}
Loading