Skip to content
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
2 changes: 1 addition & 1 deletion global/src/main/java/com/homeaid/config/SwaggerConfig.java
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,7 @@ public GroupedOpenApi reviewAPI() {
return GroupedOpenApi.builder()
.group("reviews")
.displayName("리뷰")
.pathsToMatch("/api/v1/review/**")
.pathsToMatch("/api/v1/reviews/**")
.build();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,15 @@
import com.homeaid.security.CustomUserDetails;
import com.homeaid.service.ReviewService;
import io.swagger.v3.oas.annotations.Operation;
import jakarta.validation.Valid;
import lombok.RequiredArgsConstructor;
import org.springframework.http.ResponseEntity;
import org.springframework.security.core.annotation.AuthenticationPrincipal;
import org.springframework.web.bind.annotation.*;

@RequiredArgsConstructor
@RestController
@RequestMapping("/api/v1/review")
@RequestMapping("/api/v1/reviews")
public class ReviewController {

private final ReviewService reviewService;
Expand All @@ -23,7 +24,7 @@ public class ReviewController {
@PostMapping
public ResponseEntity<CommonApiResponse<Long>> createReview(
@AuthenticationPrincipal CustomUserDetails user,
@RequestBody CustomerReviewRequestDto customerReviewRequestDto) {
@RequestBody @Valid CustomerReviewRequestDto customerReviewRequestDto) {

Review requestReview = CustomerReviewRequestDto.toEntity(customerReviewRequestDto, user.getUserRole(), user.getUserId());

Expand Down
2 changes: 1 addition & 1 deletion review/src/main/java/com/homeaid/domain/Review.java
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ public class Review {

private Long targetId;

private int rating; //별점
private Integer rating; //별점

private String comment;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,21 +3,32 @@

import com.homeaid.domain.Review;
import com.homeaid.domain.enumerate.UserRole;
import jakarta.validation.constraints.Max;
import jakarta.validation.constraints.Min;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;
import jakarta.validation.constraints.Size;
import lombok.Getter;

@Getter
public class CustomerReviewRequestDto {

@NotNull(message = "리뷰 대상자는 필수입니다")
private Long targetId;

private Integer rating;
@NotNull(message = "평점은 필수입니다")
@Min(value = 1, message = "평점은 1점 이상이어야 합니다")
@Max(value = 5, message = "평점은 5점 이하여야 합니다")
private int rating;

@NotBlank(message = "리뷰 내용은 필수입니다")
@Size(min = 10, message = "리뷰는 10자 이상 작성해주세요")
private String comment;

@NotNull(message = "예약 ID는 필수입니다")
private Long reservationId;

public static Review toEntity(CustomerReviewRequestDto customerReviewRequestDto, UserRole userRole, Long userId) {

return Review.builder()
.writerId(userId)
.targetId(customerReviewRequestDto.targetId)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,14 @@

import com.homeaid.domain.Review;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;
import org.springframework.stereotype.Repository;

@Repository
public interface ReviewRepository extends JpaRepository<Review, Long> {

@Query("SELECT COUNT(r), AVG(r.rating) FROM Review r WHERE r.targetId = :targetId")
Object[] getReviewStatisticsByTargetId(@Param("targetId") Long targetId);

}
132 changes: 73 additions & 59 deletions review/src/main/java/com/homeaid/service/ReviewServiceImpl.java
Original file line number Diff line number Diff line change
Expand Up @@ -17,81 +17,95 @@
@RequiredArgsConstructor
@Service
public class ReviewServiceImpl implements ReviewService {
private final ReviewRepository reviewRepository;
private final ReservationRepository reservationRepository;
private final MatchingRepository matchingRepository;

@Transactional
@Override
public Review createReviewByCustomer(Review requestReview) {
Reservation validatedReservation = validateReview(requestReview);
private final ReviewRepository reviewRepository;
private final ReservationRepository reservationRepository;
private final MatchingRepository matchingRepository;
private final UserRatingUpdateService userRatingUpdateService;

//예약건의 고객아이디와 요청 고객의 아이디 검증
if (!validatedReservation.getCustomerId().equals(requestReview.getWriterId())) {
throw new CustomException(ReviewErrorCode.UNAUTHORIZED_REVIEW_ACCESS);
}
@Transactional
@Override
public Review createReviewByCustomer(Review requestReview) {
Reservation validatedReservation = validateReview(requestReview);

//타켓 매니저 와 예약 건의 매니저 검증
Long reservationManagerId = getFinalMatchingOfManagerId(validatedReservation.getFinalMatchingId());
if (!reservationManagerId.equals(requestReview.getTargetId())) {
throw new CustomException(ReviewErrorCode.UNAUTHORIZED_REVIEW_TARGET);
}

//Todo 매니저 찜 기능
//예약건의 고객아이디와 요청 고객의 아이디 검증
if (!validatedReservation.getCustomerId().equals(requestReview.getWriterId())) {
throw new CustomException(ReviewErrorCode.UNAUTHORIZED_REVIEW_ACCESS);
}

return reviewRepository.save(requestReview);
//타켓 매니저 와 예약 건의 매니저 검증
Long reservationManagerId = getFinalMatchingOfManagerId(
validatedReservation.getFinalMatchingId());
if (!reservationManagerId.equals(requestReview.getTargetId())) {
throw new CustomException(ReviewErrorCode.UNAUTHORIZED_REVIEW_TARGET);
}

@Transactional
@Override
public Review createReviewByManager(Review requestReview) {
Reservation validatedReservation = validateReview(requestReview);
Review savedReview = reviewRepository.save(requestReview);
userRatingUpdateService.updateRating(requestReview.getTargetId(),
requestReview.getWriterRole());

//Todo 매니저 찜 기능

return savedReview;
}

//예약 건의 매니저와 와 요청자의 매니저 아이디 검증
Long reservationManagerId = getFinalMatchingOfManagerId(validatedReservation.getFinalMatchingId());
if (!reservationManagerId.equals(requestReview.getWriterId())) {
throw new CustomException(ReviewErrorCode.UNAUTHORIZED_REVIEW_ACCESS);
}
@Transactional
@Override
public Review createReviewByManager(Review requestReview) {
Reservation validatedReservation = validateReview(requestReview);

//예약 건의 고객아이디와 요청 받은 고객아이디 검증
if (!validatedReservation.getCustomerId().equals(requestReview.getTargetId())) {
throw new CustomException(ReviewErrorCode.UNAUTHORIZED_REVIEW_TARGET);
}
//예약 건의 매니저와 와 요청자의 매니저 아이디 검증
Long reservationManagerId = getFinalMatchingOfManagerId(
validatedReservation.getFinalMatchingId());
if (!reservationManagerId.equals(requestReview.getWriterId())) {
throw new CustomException(ReviewErrorCode.UNAUTHORIZED_REVIEW_ACCESS);
}

return reviewRepository.save(requestReview);
//예약 건의 고객아이디와 요청 받은 고객아이디 검증
if (!validatedReservation.getCustomerId().equals(requestReview.getTargetId())) {
throw new CustomException(ReviewErrorCode.UNAUTHORIZED_REVIEW_TARGET);
}

public void deleteReview(Long reviewId, Long userId) {
Review review = reviewRepository.findById(reviewId).orElseThrow(() ->
new CustomException(ReviewErrorCode.REVIEW_NOT_FOUND));
Review savedReview = reviewRepository.save(requestReview);
userRatingUpdateService.updateRating(requestReview.getTargetId(),
requestReview.getWriterRole());

return savedReview;
}

if (!review.getWriterId().equals(userId)) {
throw new CustomException(ReviewErrorCode.UNAUTHORIZED_REVIEW_ACCESS);
}
public void deleteReview(Long reviewId, Long userId) {
Review review = reviewRepository.findById(reviewId).orElseThrow(() ->
new CustomException(ReviewErrorCode.REVIEW_NOT_FOUND));

reservationRepository.deleteById(reviewId);
if (!review.getWriterId().equals(userId)) {
throw new CustomException(ReviewErrorCode.UNAUTHORIZED_REVIEW_ACCESS);
}

reviewRepository.deleteById(reviewId);
userRatingUpdateService.updateRating(review.getTargetId(), review.getWriterRole());
}

/**
* 요청 받은 예약이 유요한 예약이고 완료상태 검증
*
* @param requestReview 요청한예약 아이디
* @return 조회된 예약
*/
private Reservation validateReview(Review requestReview) {
Reservation reservation = reservationRepository.findById(requestReview.getReservationId()).orElseThrow(() ->
new CustomException(ReservationErrorCode.RESERVATION_NOT_FOUND));

if (reservation.getStatus() != ReservationStatus.COMPLETED) {
throw new CustomException(ReviewErrorCode.REVIEW_NOT_ALLOWED);
}
return reservation;
}

private Long getFinalMatchingOfManagerId(Long finalMatchingId) {
return matchingRepository.findById(finalMatchingId).orElseThrow(
() -> new CustomException(MatchingErrorCode.MATCHING_NOT_FOUND)
).getManager().getId();
/**
* 요청 받은 예약이 유요한 예약이고 완료상태 검증
*
* @param requestReview 요청한예약 아이디
* @return 조회된 예약
*/
private Reservation validateReview(Review requestReview) {
Reservation reservation = reservationRepository.findById(requestReview.getReservationId())
.orElseThrow(() ->
new CustomException(ReservationErrorCode.RESERVATION_NOT_FOUND));

if (reservation.getStatus() != ReservationStatus.COMPLETED) {
throw new CustomException(ReviewErrorCode.REVIEW_NOT_ALLOWED);
}
return reservation;
}

private Long getFinalMatchingOfManagerId(Long finalMatchingId) {
return matchingRepository.findById(finalMatchingId).orElseThrow(
() -> new CustomException(MatchingErrorCode.MATCHING_NOT_FOUND)
).getManager().getId();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package com.homeaid.service;

import com.homeaid.domain.enumerate.UserRole;

public interface UserRatingUpdateService {

void updateRating(Long targetId, UserRole writerRole);

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
package com.homeaid.service;

import com.homeaid.domain.Customer;
import com.homeaid.domain.CustomerRating;
import com.homeaid.domain.Manager;
import com.homeaid.domain.ManagerRating;
import com.homeaid.domain.enumerate.UserRole;
import com.homeaid.repository.CustomerRatingRepository;
import com.homeaid.repository.CustomerRepository;
import com.homeaid.repository.ManagerRatingRepository;
import com.homeaid.repository.ManagerRepository;
import com.homeaid.repository.ReviewRepository;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

@Service
@RequiredArgsConstructor
public class UserRatingUpdateServiceImpl implements UserRatingUpdateService {

private final ReviewRepository reviewRepository;
private final CustomerRepository customerRepository;
private final ManagerRepository managerRepository;
private final CustomerRatingRepository customerRatingRepository;
private final ManagerRatingRepository managerRatingRepository;

@Transactional
@Override
public void updateRating(Long targetId, UserRole writerRole) {
Object[] rating = reviewRepository.getReviewStatisticsByTargetId(targetId);

int count = ((Number) rating[0]).intValue();
double average = ((Number) rating[1]).doubleValue();

// 작성자: CUSTOMER -> MANAGER update / 작성자: MANAGER -> CUSTOMER update
if (writerRole == UserRole.CUSTOMER) {
updateManagerRating(targetId, count, average);
} else if (writerRole == UserRole.MANAGER) {
updateCustomerRating(targetId, count, average);
}
}

private void updateManagerRating(Long managerId, int count, double average) {
Manager manager = managerRepository.getReferenceById(managerId);
ManagerRating rating = managerRatingRepository.findById(managerId)
.orElseGet(() -> new ManagerRating(manager));
rating.updateRating(count, average);
managerRatingRepository.save(rating);
}

private void updateCustomerRating(Long customerId, int count, double average) {
Customer customer = customerRepository.getReferenceById(customerId);
CustomerRating rating = customerRatingRepository.findById(customerId)
.orElseGet(() -> new CustomerRating(customer));
rating.updateRating(count, average);
customerRatingRepository.save(rating);
}
}
53 changes: 53 additions & 0 deletions user/src/main/java/com/homeaid/domain/CustomerRating.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
package com.homeaid.domain;

import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.FetchType;
import jakarta.persistence.Id;
import jakarta.persistence.JoinColumn;
import jakarta.persistence.MapsId;
import jakarta.persistence.OneToOne;
import jakarta.persistence.Table;
import java.time.LocalDateTime;
import lombok.Getter;
import lombok.NoArgsConstructor;
import org.springframework.data.annotation.LastModifiedDate;

@Entity
@Getter
@Table(name = "customer_rating")
@NoArgsConstructor
public class CustomerRating {

@Id
@Column(name = "customer_id")
private Long customerId;

@Column(name = "review_count", nullable = false)
private Integer reviewCount;

@Column(name = "average_rating", nullable = false)
private Double averageRating;

@Column(name = "last_updated", nullable = false)
@LastModifiedDate
private LocalDateTime lastUpdated;

@OneToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "customer_id")
@MapsId // customer_id 를 PK로 사용
private Customer customer;

public CustomerRating(Customer customer){
this.customerId = customer.getId();
this.reviewCount = 0;
this.averageRating = 0.0;
this.customer = customer;
}

public void updateRating(int reviewCount, double averageRating){
this.averageRating = averageRating;
this.reviewCount = reviewCount;
}

}
Loading