Skip to content

Commit

Permalink
refactor/#68 noise entity에서 review 분리 (#69)
Browse files Browse the repository at this point in the history
  • Loading branch information
minjo-on authored Jan 20, 2025
1 parent 4273c96 commit 16cf479
Show file tree
Hide file tree
Showing 18 changed files with 426 additions and 10 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ public class NoiseApiController {
- Description : 이 API는 해당 소음 데이터를 조회합니다.
""")
@ApiResponse(responseCode = "200")
@GetMapping("/noise/{noiseId}")
@GetMapping("/{noiseId}")
public ResponseEntity<NoiseSummaryResponse> getUserNoiseDetail(
@Parameter(description = "조회할 noise 데이터의 ID", example = "10", required = true)
@PathVariable String noiseId
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ public record NoisePersistResponse(
String id
) {
public static NoisePersistResponse from(Noise noise){
return builder()
return NoisePersistResponse.builder()
.id(noise.extractUuid())
.build();
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
package sorisoop.soridam.api.review;

import static org.springframework.http.HttpStatus.CREATED;

import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.responses.ApiResponse;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.validation.Valid;
import lombok.RequiredArgsConstructor;
import sorisoop.soridam.api.review.application.ReviewFacade;
import sorisoop.soridam.api.review.presentation.request.ReviewCreateRequest;
import sorisoop.soridam.api.review.presentation.request.ReviewUpdateRequest;
import sorisoop.soridam.api.review.presentation.response.ReviewPersistResponse;

@RestController
@RequiredArgsConstructor
@RequestMapping("/api/reviews")
@Tag(name = "Review", description = "리뷰 API")
public class ReviewApiController {
private final ReviewFacade reviewFacade;

@Operation(summary = "리뷰 생성 API", description = "리뷰를 생성합니다.")
@ApiResponse(responseCode = "201", description = "리뷰 생성 성공")
@PostMapping
public ResponseEntity<ReviewPersistResponse> createReview(
@Valid @RequestBody ReviewCreateRequest request) {
ReviewPersistResponse response = reviewFacade.create(request);
return ResponseEntity.status(CREATED).body(response);
}

@Operation(summary = "리뷰 업데이트 API", description = "리뷰를 업데이트합니다.")
@ApiResponse(responseCode = "200", description = "리뷰 업데이트 성공")
@PutMapping("/{reviewId}")
public ResponseEntity<Void> updateReview(
@Parameter(description = "리뷰 ID", example = "123", required = true)
@PathVariable String reviewId,
@Valid @RequestBody ReviewUpdateRequest request) {
reviewFacade.update(reviewId, request);
return ResponseEntity.ok().build();
}

@Operation(summary = "리뷰 삭제 API", description = "리뷰를 삭제합니다.")
@ApiResponse(responseCode = "204", description = "리뷰 삭제 성공")
@DeleteMapping("/{reviewId}")
public ResponseEntity<Void> deleteReview(
@Parameter(description = "리뷰 ID", example = "123", required = true)
@PathVariable String reviewId) {
reviewFacade.delete(reviewId);
return ResponseEntity.noContent().build();
}
}

Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
package sorisoop.soridam.api.review.application;

import org.springframework.stereotype.Component;

import lombok.RequiredArgsConstructor;
import sorisoop.soridam.api.review.presentation.request.ReviewCreateRequest;
import sorisoop.soridam.api.review.presentation.request.ReviewUpdateRequest;
import sorisoop.soridam.api.review.presentation.response.ReviewPersistResponse;
import sorisoop.soridam.domain.review.application.ReviewCommandService;
import sorisoop.soridam.domain.review.application.ReviewQueryService;
import sorisoop.soridam.domain.review.domain.Review;
import sorisoop.soridam.domain.user.application.UserQueryService;
import sorisoop.soridam.domain.user.domain.User;

@Component
@RequiredArgsConstructor
public class ReviewFacade {
private final ReviewCommandService reviewCommandService;
private final ReviewQueryService reviewQueryService;
private final UserQueryService userQueryService;

public ReviewPersistResponse create(ReviewCreateRequest request) {
User author = userQueryService.me();

Review review = reviewCommandService.create(
request.targetId(),
request.reviewType(),
author.getId(),
request.content(),
request.rating()
);

return ReviewPersistResponse.from(review);
}

public void update(String reviewId, ReviewUpdateRequest request) {
User user = userQueryService.me();
Review review = reviewQueryService.getById(reviewId);
reviewCommandService.update(user, review, request.content(), request.rating());
}

public void delete(String reviewId) {
User user = userQueryService.me();
Review review = reviewQueryService.getById(reviewId);
reviewCommandService.delete(user, review);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
package sorisoop.soridam.api.review.presentation.request;

import static io.swagger.v3.oas.annotations.media.Schema.RequiredMode.REQUIRED;

import java.math.BigDecimal;

import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.DecimalMax;
import jakarta.validation.constraints.DecimalMin;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;
import lombok.Builder;
import sorisoop.soridam.globalutil.uuid.UuidPrefix;

@Builder
public record ReviewCreateRequest(
@Schema(description = "게시글 ID", example = "uuid", requiredMode = REQUIRED)
@NotBlank
String targetId,

@Schema(description = "게시글 타입", example = "NOISE", requiredMode = REQUIRED)
@NotNull
UuidPrefix reviewType,

@Schema(description = "작성 내용", example = "content", requiredMode = REQUIRED)
String content,

@Schema(description = "평점", example = "5.0", requiredMode = REQUIRED)
@NotNull
@DecimalMin(value = "0.0")
@DecimalMax(value = "5.0")
BigDecimal rating
) {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package sorisoop.soridam.api.review.presentation.request;

import static io.swagger.v3.oas.annotations.media.Schema.RequiredMode.REQUIRED;

import java.math.BigDecimal;

import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.DecimalMax;
import jakarta.validation.constraints.DecimalMin;
import jakarta.validation.constraints.NotNull;

public record ReviewUpdateRequest(
@Schema(description = "수정한 작성 내용", example = "content", requiredMode = REQUIRED)
String content,

@Schema(description = "평점", example = "5.0", requiredMode = REQUIRED)
@NotNull
@DecimalMin(value = "0.0")
@DecimalMax(value = "5.0")
BigDecimal rating
) {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package sorisoop.soridam.api.review.presentation.response;

import static io.swagger.v3.oas.annotations.media.Schema.RequiredMode.REQUIRED;

import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Builder;
import sorisoop.soridam.domain.review.domain.Review;

@Builder
public record ReviewPersistResponse(
@Schema(description = "review ID", example = "asdfjklsadjklsamlsdfsldm", requiredMode = REQUIRED)
String id
) {
public static ReviewPersistResponse from(Review review){
return ReviewPersistResponse.builder()
.id(review.extractUuid())
.build();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
.requestMatchers(STATIC_RESOURCES_PATTERNS).permitAll()
.requestMatchers(PERMIT_ALL_PATTERNS).permitAll()
.requestMatchers(PUBLIC_ENDPOINTS).permitAll()
.anyRequest().authenticated()
.anyRequest().permitAll()
)
.exceptionHandling(exceptions -> exceptions
.authenticationEntryPoint(jwtAuthenticationEntryPoint)
Expand Down Expand Up @@ -87,12 +87,10 @@ public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
CorsConfigurationSource corsConfigurationSource() {
return request -> {
CorsConfiguration config = new CorsConfiguration();
config.setAllowedHeaders(Collections.singletonList("*"));
config.setAllowedMethods(Collections.singletonList("*"));
config.setAllowedOriginPatterns(Arrays.asList(
"*" // TODO: CORS 설정 변경 필요
));
config.setAllowCredentials(true);
config.setAllowedHeaders(Collections.singletonList("*")); // 모든 헤더 허용
config.setAllowedMethods(Arrays.asList("GET", "POST", "PUT", "DELETE")); // 필요한 메서드만 허용
config.setAllowedOriginPatterns(Arrays.asList("*")); // 특정 도메인 허용
config.setAllowCredentials(true); // 인증 정보 포함 허용
return config;
};
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
package sorisoop.soridam.domain.review.application;

import java.math.BigDecimal;

import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import lombok.RequiredArgsConstructor;
import sorisoop.soridam.domain.review.domain.Review;
import sorisoop.soridam.domain.review.domain.ReviewRepository;
import sorisoop.soridam.domain.user.domain.User;
import sorisoop.soridam.domain.user.exception.InvalidUserException;
import sorisoop.soridam.globalutil.user.UserUtil;
import sorisoop.soridam.globalutil.uuid.UuidPrefix;

@Service
@Transactional
@RequiredArgsConstructor
public class ReviewCommandService {
private final ReviewRepository reviewRepository;

public Review create(String targetId, UuidPrefix reviewType,
String authorId, String content, BigDecimal rating) {
Review review = Review.create(targetId, reviewType, authorId, content, rating);
return reviewRepository.save(review);
}

public void update(User user, Review review, String content, BigDecimal rating) {
validateUser(user.getId(), review.getAuthorId());
review.updateContent(content);
review.updateRating(rating);
}

public void delete(User user, Review review) {
validateUser(user.getId(), review.getAuthorId());
reviewRepository.delete(review);
}

private void validateUser(String user1, String user2) {
if (!UserUtil.isSameUser(user1, user2)) {
throw new InvalidUserException();
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package sorisoop.soridam.domain.review.application;

import org.springframework.stereotype.Service;

import lombok.RequiredArgsConstructor;
import sorisoop.soridam.domain.review.domain.Review;
import sorisoop.soridam.domain.review.domain.ReviewRepository;
import sorisoop.soridam.domain.review.exception.ReviewNotFoundException;

@Service
@RequiredArgsConstructor
public class ReviewQueryService {
private final ReviewRepository reviewRepository;

public Review getById(String id) {
return reviewRepository.findById(id)
.orElseThrow(ReviewNotFoundException::new);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
package sorisoop.soridam.domain.review.domain;

import static jakarta.persistence.EnumType.STRING;
import static lombok.AccessLevel.PROTECTED;
import static sorisoop.soridam.globalutil.uuid.UuidPrefix.REVIEW;
import static sorisoop.soridam.globalutil.uuid.UuidPrefix.USER;

import java.math.BigDecimal;

import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.Enumerated;
import jakarta.persistence.Id;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;
import sorisoop.soridam.common.domain.BaseTimeEntity;
import sorisoop.soridam.common.domain.UuidExtractable;
import sorisoop.soridam.globalutil.uuid.PrefixedUuid;
import sorisoop.soridam.globalutil.uuid.UuidPrefix;

@Entity
@Getter
@Builder
@NoArgsConstructor(access = PROTECTED)
@AllArgsConstructor(access = PROTECTED)
public class Review extends BaseTimeEntity implements UuidExtractable {
@Id
@PrefixedUuid(REVIEW)
private String id;

@Column(nullable = false)
private String targetId;

@Enumerated(STRING)
@Column(nullable = false, length = 25)
private UuidPrefix reviewType;

@Column(nullable = false)
private String authorId;

@Column(nullable = false, columnDefinition = "TEXT")
private String content;

@Column(nullable = false, precision = 2, scale = 1)
private BigDecimal rating;

public static Review create(String targetId, UuidPrefix reviewType, String authorId, String content, BigDecimal rating) {
return Review.builder()
.targetId(reviewType.getPrefix() + targetId)
.reviewType(reviewType)
.authorId(USER.getPrefix() + authorId)
.content(content)
.rating(rating)
.build();
}

public void updateContent(String content) {
this.content = content;
}

public void updateRating(BigDecimal rating) {
this.rating = rating;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package sorisoop.soridam.domain.review.domain;

import java.util.Optional;

public interface ReviewRepository {
Review save(Review review);

Optional<Review> findById(String id);

void delete(Review review);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package sorisoop.soridam.domain.review.domain;

import lombok.AllArgsConstructor;
import lombok.Getter;

@Getter
@AllArgsConstructor
public enum ReviewType {
NOISE("소음"),
;

private String description;
}
Loading

0 comments on commit 16cf479

Please sign in to comment.