Skip to content

Commit

Permalink
Merge pull request #301 from linglong67/feature/washzone-review-api
Browse files Browse the repository at this point in the history
[feat] 세차장 리뷰 기능 + 리뷰 기능 보완
  • Loading branch information
linglong67 committed Mar 15, 2024
2 parents 8cabb90 + 00ab661 commit 95a0d83
Show file tree
Hide file tree
Showing 23 changed files with 885 additions and 37 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(acceptInterceptor)
.addPathPatterns("/auth/**") //** 인증 JWT 토큰 관련 **//
.addPathPatterns("/reviews/**")
.addPathPatterns("/reviews-washzone/**")
.addPathPatterns("/likes/**")
.addPathPatterns("/mypage/**");
//.excludePathPatterns("/public/**"); // 제외할 URL 패턴
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,11 @@
@RequiredArgsConstructor
public enum ReviewErrorCode implements ErrorCode {
INVALID_STAR_RATING_VALUE(HttpStatus.BAD_REQUEST.value(), "ERV001", "유효하지 않은 별점입니다."),
INVALID_REVIEW_WRITE_REQUEST(HttpStatus.BAD_REQUEST.value(), "ERV002", "리뷰가 중복되거나 유효하지 않습니다."),
NOT_FOUND_REVIEW(HttpStatus.BAD_REQUEST.value(), "ERV003", "리뷰가 존재하지 않습니다.");
DUPLICATE_REVIEW_EXISTS(HttpStatus.BAD_REQUEST.value(), "ERV002", "중복된 리뷰가 존재합니다."),
NOT_FOUND_REVIEW(HttpStatus.BAD_REQUEST.value(), "ERV003", "리뷰가 존재하지 않습니다."),
NOT_FOUND_PRODUCT_FOR_REVIEW_CREATION(HttpStatus.BAD_REQUEST.value(), "ERV004", "제품이 존재하지 않습니다."),
NOT_FOUND_MEMBER_FOR_REVIEW_CREATION(HttpStatus.BAD_REQUEST.value(), "ERV005", "회원이 존재하지 않습니다."),
MISMATCHED_MEMBER_NO_AND_ID(HttpStatus.BAD_REQUEST.value(), "ERV006", "회원 번호와 아이디가 일치하지 않습니다.");

private final int status;
private final String code;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@

import com.kernel360.response.ApiResponse;
import com.kernel360.review.code.ReviewBusinessCode;
import com.kernel360.review.dto.ReviewResponseDto;
import com.kernel360.review.dto.ReviewRequestDto;
import com.kernel360.review.dto.ReviewResponseDto;
import com.kernel360.review.service.ReviewService;
import jakarta.validation.Valid;
import lombok.RequiredArgsConstructor;
Expand Down Expand Up @@ -46,27 +46,31 @@ public ResponseEntity<ApiResponse<ReviewResponseDto>> getReview(@PathVariable Lo
return ApiResponse.toResponseEntity(ReviewBusinessCode.SUCCESS_GET_REVIEW, reviewService.getReview(reviewNo));
}

@PostMapping("")
@PostMapping
public <T> ResponseEntity<ApiResponse<T>> createReview(
@Valid @RequestPart ReviewRequestDto review,
@RequestPart(required = false) List<MultipartFile> files) {
reviewService.createReview(review, files);
@RequestPart(required = false) List<MultipartFile> files,
@RequestHeader("Id") String id) {
reviewService.createReview(review, files, id);

return ApiResponse.toResponseEntity(ReviewBusinessCode.SUCCESS_CREATE_REVIEW);
}

@PatchMapping("")
@PatchMapping
public <T> ResponseEntity<ApiResponse<T>> updateReview(
@Valid @RequestPart ReviewRequestDto review,
@RequestPart(required = false) List<MultipartFile> files) {
reviewService.updateReview(review, files);
@Valid @RequestPart ReviewRequestDto review,
@RequestPart(required = false) List<MultipartFile> files,
@RequestHeader("Id") String id) {
reviewService.updateReview(review, files, id);

return ApiResponse.toResponseEntity(ReviewBusinessCode.SUCCESS_UPDATE_REVIEW);
}

@DeleteMapping("/{reviewNo}")
public <T> ResponseEntity<ApiResponse<T>> deleteReview(@PathVariable Long reviewNo) {
reviewService.deleteReview(reviewNo);
public <T> ResponseEntity<ApiResponse<T>> deleteReview(
@PathVariable Long reviewNo,
@RequestHeader("Id") String id) {
reviewService.deleteReview(reviewNo, id);

return ApiResponse.toResponseEntity(ReviewBusinessCode.SUCCESS_DELETE_REVIEW);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import com.kernel360.file.entity.File;
import com.kernel360.file.entity.FileReferType;
import com.kernel360.file.repository.FileRepository;
import com.kernel360.member.repository.MemberRepository;
import com.kernel360.review.code.ReviewErrorCode;
import com.kernel360.review.dto.ReviewRequestDto;
import com.kernel360.review.dto.ReviewResponseDto;
Expand Down Expand Up @@ -33,6 +34,7 @@
public class ReviewService {

private final ReviewRepository reviewRepository;
private final MemberRepository memberRepository;
private final FileRepository fileRepository;
private final FileUtils fileUtils;

Expand All @@ -45,23 +47,23 @@ public class ReviewService {

@Transactional(readOnly = true)
public Page<ReviewResponseDto> getReviewsByProduct(Long productNo, String sortBy, Pageable pageable) {
log.info("제품 리뷰 목록 조회 -> product_no {}", productNo);
log.info("제품별 리뷰 목록 조회 -> product_no {}", productNo);

return reviewRepository.findAllByCondition(ReviewSearchDto.byProductNo(productNo, sortBy), pageable)
.map(ReviewSearchResult::toDto);
}

@Transactional(readOnly = true)
public Page<ReviewResponseDto> getReviewsByMember(Long memberNo, String sortBy, Pageable pageable) {
log.info("멤버 리뷰 목록 조회 -> memberNo {}", memberNo);
log.info("멤버별 제품 리뷰 목록 조회 -> member_no {}", memberNo);

return reviewRepository.findAllByCondition(ReviewSearchDto.byMemberNo(memberNo, sortBy), pageable)
.map(ReviewSearchResult::toDto);
}

@Transactional(readOnly = true)
public ReviewResponseDto getReview(Long reviewNo) {
log.info("리뷰 단건 조회 -> review_no {}", reviewNo);
log.info("제품 리뷰 단건 조회 -> review_no {}", reviewNo);
ReviewSearchResult review = reviewRepository.findByReviewNo(reviewNo);

if (Objects.isNull(review)) {
Expand All @@ -72,20 +74,31 @@ public ReviewResponseDto getReview(Long reviewNo) {
}

@Transactional
public Review createReview(ReviewRequestDto reviewRequestDto, List<MultipartFile> files) {
public Review createReview(ReviewRequestDto reviewRequestDto, List<MultipartFile> files, String id) {
isValidMemberInfo(id, reviewRequestDto.memberNo());
isValidStarRating(reviewRequestDto.starRating());

Review review;

try {
review = reviewRepository.saveAndFlush(reviewRequestDto.toEntity());
log.info("리뷰 등록 -> review_no {}", review.getReviewNo());
log.info("제품 리뷰 등록 -> review_no {}", review.getReviewNo());

if (Objects.nonNull(files)) {
uploadFiles(files, reviewRequestDto.productNo(), review.getReviewNo());
}
} catch (DataIntegrityViolationException e) {
throw new BusinessException(ReviewErrorCode.INVALID_REVIEW_WRITE_REQUEST);
String msg = e.getMessage().toString();

if (msg.contains("review_product_no_fkey")) {
throw new BusinessException(ReviewErrorCode.NOT_FOUND_PRODUCT_FOR_REVIEW_CREATION);
}

if (msg.contains("review_member_no_fkey")) {
throw new BusinessException(ReviewErrorCode.NOT_FOUND_MEMBER_FOR_REVIEW_CREATION);
}

throw new BusinessException(ReviewErrorCode.DUPLICATE_REVIEW_EXISTS);
}

return review;
Expand All @@ -98,51 +111,53 @@ private void uploadFiles(List<MultipartFile> files, Long productNo, Long reviewN
String fileUrl = String.join("/", bucketUrl, fileKey);

File fileInfo = fileRepository.save(File.of(null, file.getOriginalFilename(), fileKey, fileUrl, REVIEW_CODE, reviewNo));
log.info("리뷰 파일 등록 -> file_no {}", fileInfo.getFileNo());
log.info("제품 리뷰 파일 등록 -> file_no {}", fileInfo.getFileNo());
});
}

@Transactional
public void updateReview(ReviewRequestDto reviewRequestDto, List<MultipartFile> files) {
public void updateReview(ReviewRequestDto reviewRequestDto, List<MultipartFile> files, String id) {
Review review = isVisibleReview(reviewRequestDto.reviewNo());
long productNo = review.getProduct().getProductNo();

isValidMemberInfo(id, review.getMember().getMemberNo());
isValidStarRating(reviewRequestDto.starRating());

long productNo = review.getProduct().getProductNo();

try {
reviewRepository.saveAndFlush(reviewRequestDto.toEntityForUpdate());
log.info("리뷰 수정 -> review_no {}", reviewRequestDto.reviewNo());
log.info("제품 리뷰 수정 -> review_no {}", reviewRequestDto.reviewNo());

fileRepository.findByReferenceTypeAndReferenceNo(REVIEW_CODE, reviewRequestDto.reviewNo())
.stream()
.forEach(file -> {
if (!reviewRequestDto.files().contains(file.getFileUrl())) {
fileUtils.delete(file.getFileKey());
fileRepository.deleteById(file.getFileNo());
log.info("리뷰 파일 삭제 -> file_no {}", file.getFileNo());
log.info("제품 리뷰 파일 삭제 -> file_no {}", file.getFileNo());
}
});

if (Objects.nonNull(files)) {
uploadFiles(files, productNo, reviewRequestDto.reviewNo());
}
} catch (DataIntegrityViolationException e) {
throw new BusinessException(ReviewErrorCode.INVALID_REVIEW_WRITE_REQUEST);
throw new BusinessException(ReviewErrorCode.DUPLICATE_REVIEW_EXISTS);
}
}

@Transactional
public void deleteReview(Long reviewNo) {
isVisibleReview(reviewNo);
public void deleteReview(Long reviewNo, String id) {
Review review = isVisibleReview(reviewNo);
isValidMemberInfo(id, review.getMember().getMemberNo());

reviewRepository.deleteById(reviewNo);
log.info("리뷰 삭제 -> review_no {}", reviewNo);
log.info("제품 리뷰 삭제 -> review_no {}", reviewNo);

fileRepository.findByReferenceTypeAndReferenceNo(REVIEW_CODE, reviewNo)
.stream()
.forEach(file -> {
fileUtils.delete(file.getFileKey());
log.info("리뷰 파일 삭제 -> file_no {}", file.getFileNo());
log.info("제품 리뷰 파일 삭제 -> file_no {}", file.getFileNo());
});
fileRepository.deleteByReferenceTypeAndReferenceNo(REVIEW_CODE, reviewNo);
}
Expand All @@ -157,6 +172,11 @@ private Review isVisibleReview(Long reviewNo) {
return review.get();
}

private void isValidMemberInfo(String id, Long memberNo) {
memberRepository.findOneByIdAndMemberNo(id, memberNo)
.orElseThrow(() -> new BusinessException(ReviewErrorCode.MISMATCHED_MEMBER_NO_AND_ID));
}

private void isValidStarRating(BigDecimal starRating) {
if (BigDecimal.ZERO.compareTo(starRating) > 0) {
throw new BusinessException(ReviewErrorCode.INVALID_STAR_RATING_VALUE);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
* DTO for {@link com.kernel360.washzone.entity.WashZone}
*/
public record WashZoneDto(
Long washZoneNo,
String name,
String address,
Double latitude,
Expand All @@ -23,6 +24,7 @@ public static WashZoneDto of (
String remarks
){
return new WashZoneDto(
null,
name,
address,
latitude,
Expand Down Expand Up @@ -52,4 +54,21 @@ public WashZone toEntity(){
this.remarks
);
}

/** find review **/
public static WashZoneDto of(
Long washZoneNo,
String name,
String address,
String type
){
return new WashZoneDto(
washZoneNo,
name,
address,
null,
null,
type,
null);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
package com.kernel360.washzonereview.code;

import com.kernel360.code.BusinessCode;
import lombok.RequiredArgsConstructor;
import org.springframework.http.HttpStatus;

@RequiredArgsConstructor
public enum WashzoneReviewBusinessCode implements BusinessCode {
SUCCESS_GET_WASHZONE_REVIEWS(HttpStatus.OK.value(), "BWZRV001", "세차장 리뷰 목록 조회 성공"),
SUCCESS_GET_WASHZONE_REVIEW(HttpStatus.OK.value(), "BWZRV002", "세차장 리뷰 단건 조회 성공"),
SUCCESS_CREATE_WASHZONE_REVIEW(HttpStatus.OK.value(), "BWZRV003", "세차장 리뷰 등록 성공"),
SUCCESS_UPDATE_WASHZONE_REVIEW(HttpStatus.OK.value(), "BWZRV004", "세차장 리뷰 수정 성공"),
SUCCESS_DELETE_WASHZONE_REVIEW(HttpStatus.OK.value(), "BWZRV005", "세차장 리뷰 삭제 성공");

private final int status;
private final String code;
private final String message;

@Override
public int getStatus() {
return status;
}

@Override
public String getCode() {
return code;
}

@Override
public String getMessage() {
return message;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
package com.kernel360.washzonereview.code;

import com.kernel360.code.ErrorCode;
import lombok.RequiredArgsConstructor;
import org.springframework.http.HttpStatus;

@RequiredArgsConstructor
public enum WashzoneReviewErrorCode implements ErrorCode {
INVALID_STAR_RATING_VALUE(HttpStatus.BAD_REQUEST.value(), "EWZRV001", "유효하지 않은 별점입니다."),
DUPLICATE_WASHZONE_REVIEW_EXISTS(HttpStatus.BAD_REQUEST.value(), "EWZRV002", "중복된 세차장 리뷰가 존재합니다."),
NOT_FOUND_WASHZONE_REVIEW(HttpStatus.BAD_REQUEST.value(), "EWZRV003", "세차장 리뷰가 존재하지 않습니다."),
NOT_FOUND_WASHZONE_FOR_WASHZONE_REVIEW_CREATION(HttpStatus.BAD_REQUEST.value(), "EWZRV004", "세차장이 존재하지 않습니다."),
NOT_FOUND_MEMBER_FOR_WASHZONE_REVIEW_CREATION(HttpStatus.BAD_REQUEST.value(), "EWZRV005", "회원이 존재하지 않습니다."),
MISMATCHED_MEMBER_NO_AND_ID(HttpStatus.BAD_REQUEST.value(), "ERV006", "회원 번호와 아이디가 일치하지 않습니다.");

private final int status;
private final String code;
private final String message;

@Override
public int getStatus() {
return status;
}

@Override
public String getCode() {
return code;
}

@Override
public String getMessage() {
return message;
}
}
Loading

0 comments on commit 95a0d83

Please sign in to comment.