Skip to content

Commit

Permalink
[Release] 리뷰미 v1.1.2 배포
Browse files Browse the repository at this point in the history
[Release] 리뷰미 v1.1.2 배포
  • Loading branch information
donghoony authored Oct 7, 2024
2 parents 983a97d + b8dd2db commit 722c603
Show file tree
Hide file tree
Showing 34 changed files with 1,572 additions and 2 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
package reviewme.review.domain.abstraction;

import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
import jakarta.persistence.Inheritance;
import jakarta.persistence.InheritanceType;
import jakarta.persistence.Table;
import lombok.AccessLevel;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.NoArgsConstructor;

@Entity
@Table(name = "answer")
@Inheritance(strategy = InheritanceType.JOINED)
@NoArgsConstructor(access = AccessLevel.PROTECTED)
@EqualsAndHashCode(of = "id")
@Getter
public abstract class Answer {

@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
protected Long id;

@Column(name = "question_id", nullable = false)
protected long questionId;

@Column(name = "review_id", nullable = false, insertable = false, updatable = false)
private long reviewId;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
package reviewme.review.domain.abstraction;

import jakarta.persistence.CascadeType;
import jakarta.persistence.Entity;
import jakarta.persistence.FetchType;
import jakarta.persistence.JoinColumn;
import jakarta.persistence.OneToMany;
import jakarta.persistence.Table;
import java.util.List;
import lombok.AccessLevel;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.NoArgsConstructor;
import reviewme.review.domain.exception.QuestionNotAnsweredException;

@Entity
@Table(name = "new_checkbox_answer")
@NoArgsConstructor(access = AccessLevel.PROTECTED)
@EqualsAndHashCode(callSuper = true)
@Getter
public class NewCheckboxAnswer extends Answer {

@OneToMany(fetch = FetchType.EAGER, cascade = CascadeType.ALL, orphanRemoval = true)
@JoinColumn(name = "checkbox_answer_id", nullable = false, updatable = false)
private List<NewCheckboxAnswerSelectedOption> selectedOptionIds;

public NewCheckboxAnswer(long questionId, List<Long> selectedOptionIds) {
validateSelectedOptionIds(questionId, selectedOptionIds);
this.questionId = questionId;
this.selectedOptionIds = selectedOptionIds.stream()
.map(NewCheckboxAnswerSelectedOption::new)
.toList();
}

private void validateSelectedOptionIds(long questionId, List<Long> selectedOptionIds) {
if (selectedOptionIds == null || selectedOptionIds.isEmpty()) {
throw new QuestionNotAnsweredException(questionId);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package reviewme.review.domain.abstraction;

import org.springframework.data.jpa.repository.JpaRepository;

public interface NewCheckboxAnswerRepository extends JpaRepository<NewCheckboxAnswer, Long> {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
package reviewme.review.domain.abstraction;

import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
import jakarta.persistence.Table;
import lombok.AccessLevel;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.NoArgsConstructor;

@Entity
@Table(name = "new_checkbox_answer_selected_option")
@NoArgsConstructor(access = AccessLevel.PROTECTED)
@EqualsAndHashCode(of = "id")
@Getter
public class NewCheckboxAnswerSelectedOption {

@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;

@Column(name = "checkbox_answer_id", nullable = false, insertable = false, updatable = false)
private long checkboxAnswerId;

@Column(name = "selected_option_id", nullable = false)
private long selectedOptionId;

public NewCheckboxAnswerSelectedOption(long selectedOptionId) {
this.selectedOptionId = selectedOptionId;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
package reviewme.review.domain.abstraction;

import jakarta.persistence.CascadeType;
import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.FetchType;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
import jakarta.persistence.JoinColumn;
import jakarta.persistence.OneToMany;
import jakarta.persistence.Table;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
import lombok.AccessLevel;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.NoArgsConstructor;

@Entity
@Table(name = "new_review")
@NoArgsConstructor(access = AccessLevel.PROTECTED)
@EqualsAndHashCode(of = "id")
@Getter
public class NewReview {

@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;

@Column(name = "template_id", nullable = false)
private long templateId;

@Column(name = "review_group_id", nullable = false)
private long reviewGroupId;

@OneToMany(fetch = FetchType.EAGER, cascade = CascadeType.PERSIST)
@JoinColumn(name = "review_id", nullable = false, updatable = false)
private List<Answer> answers;

@Column(name = "created_at", nullable = false)
private LocalDateTime createdAt;

public NewReview(long templateId, long reviewGroupId, List<Answer> answers) {
this.templateId = templateId;
this.reviewGroupId = reviewGroupId;
this.answers = answers;
this.createdAt = LocalDateTime.now();
}

public Set<Long> getAnsweredQuestionIds() {
return answers.stream()
.map(Answer::getQuestionId)
.collect(Collectors.toSet());
}

public boolean hasAnsweredQuestion(long questionId) {
return getAnsweredQuestionIds().contains(questionId);
}

public <T extends Answer> List<T> getAnswersByType(Class<T> clazz) {
return answers.stream()
.filter(clazz::isInstance)
.map(clazz::cast)
.toList();
}

public LocalDate getCreatedDate() {
return createdAt.toLocalDate();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
package reviewme.review.domain.abstraction;

import java.time.LocalDate;
import java.util.List;
import java.util.Optional;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;

public interface NewReviewRepository extends JpaRepository<NewReview, Long> {

@Query(value = """
SELECT r.* FROM new_review r
WHERE r.review_group_id = :reviewGroupId
ORDER BY r.created_at DESC
""", nativeQuery = true)
List<NewReview> findAllByGroupId(long reviewGroupId);

@Query(value = """
SELECT r.* FROM new_review r
WHERE r.review_group_id = :reviewGroupId
AND (:lastReviewId IS NULL OR r.id < :lastReviewId)
ORDER BY r.created_at DESC, r.id DESC
LIMIT :limit
""", nativeQuery = true)
List<NewReview> findByReviewGroupIdWithLimit(long reviewGroupId, Long lastReviewId, int limit);

Optional<NewReview> findByIdAndReviewGroupId(long reviewId, long reviewGroupId);

@Query(value = """
SELECT COUNT(r.id) FROM new_review r
WHERE r.review_group_id = :reviewGroupId
AND r.id < :reviewId
AND CAST(r.created_at AS DATE) <= :createdDate
""", nativeQuery = true)
Long existsOlderReviewInGroupInLong(long reviewGroupId, long reviewId, LocalDate createdDate);

default boolean existsOlderReviewInGroup(long reviewGroupId, long reviewId, LocalDate createdDate) {
return existsOlderReviewInGroupInLong(reviewGroupId, reviewId, createdDate) > 0;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
package reviewme.review.domain.abstraction;

import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.Table;
import lombok.AccessLevel;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.NoArgsConstructor;
import reviewme.review.domain.exception.QuestionNotAnsweredException;

@Entity
@Table(name = "new_text_answer")
@NoArgsConstructor(access = AccessLevel.PROTECTED)
@EqualsAndHashCode(callSuper = true)
@Getter
public class NewTextAnswer extends Answer {

@Column(name = "content", nullable = false, length = 5000)
private String content;

public NewTextAnswer(long questionId, String content) {
validateContent(questionId, content);
this.questionId = questionId;
this.content = content;
}

private void validateContent(long questionId, String content) {
if (content == null || content.isEmpty()) {
throw new QuestionNotAnsweredException(questionId);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package reviewme.review.domain.abstraction;

import org.springframework.data.jpa.repository.JpaRepository;

public interface NewTextAnswerRepository extends JpaRepository<NewTextAnswer, Long> {
}
Original file line number Diff line number Diff line change
@@ -1,10 +1,16 @@
package reviewme.review.service;

import lombok.RequiredArgsConstructor;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import reviewme.review.domain.Review;
import reviewme.review.domain.abstraction.NewReview;
import reviewme.review.domain.abstraction.NewReviewRepository;
import reviewme.review.repository.ReviewRepository;
import reviewme.review.service.abstraction.mapper.NewReviewMapper;
import reviewme.review.service.abstraction.validator.NewReviewValidator;
import reviewme.review.service.dto.request.ReviewRegisterRequest;
import reviewme.review.service.mapper.ReviewMapper;
import reviewme.review.service.validator.ReviewValidator;
Expand All @@ -13,16 +19,28 @@
@RequiredArgsConstructor
public class ReviewRegisterService {

private static final Logger log = LoggerFactory.getLogger(ReviewRegisterService.class);
private final ReviewMapper reviewMapper;
private final ReviewValidator reviewValidator;

private final ReviewRepository reviewRepository;

// 리뷰 추상화, 같은 Transactional에 넣어 처리
private final NewReviewMapper newReviewMapper;
private final NewReviewValidator newReviewValidator;
private final NewReviewRepository newReviewRepository;

@Transactional
public long registerReview(ReviewRegisterRequest request) {
Review review = reviewMapper.mapToReview(request);
reviewValidator.validate(review);
Review registeredReview = reviewRepository.save(review);

// 새로운 테이블에 중복해서 저장
NewReview newReview = newReviewMapper.mapToReview(request);
newReviewValidator.validate(newReview);
newReviewRepository.save(newReview);

return registeredReview.getId();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package reviewme.review.service.abstraction.mapper;

import java.util.List;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Component;
import reviewme.question.domain.QuestionType;

@Component
@RequiredArgsConstructor
public class AnswerMapperFactory {

private final List<NewAnswerMapper> answerMappers;

public NewAnswerMapper getAnswerMapper(QuestionType questionType) {
return answerMappers.stream()
.filter(answerMapper -> answerMapper.supports(questionType))
.findFirst()
.orElseThrow(() -> new UnsupportedQuestionTypeException(questionType));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package reviewme.review.service.abstraction.mapper;

import reviewme.question.domain.QuestionType;
import reviewme.review.domain.abstraction.Answer;
import reviewme.review.service.dto.request.ReviewAnswerRequest;

public interface NewAnswerMapper {

boolean supports(QuestionType questionType);

Answer mapToAnswer(ReviewAnswerRequest answerRequest);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package reviewme.review.service.abstraction.mapper;

import org.springframework.stereotype.Component;
import reviewme.question.domain.QuestionType;
import reviewme.review.domain.abstraction.NewCheckboxAnswer;
import reviewme.review.service.dto.request.ReviewAnswerRequest;
import reviewme.review.service.exception.CheckBoxAnswerIncludedTextException;

@Component
public class NewCheckboxAnswerMapper implements NewAnswerMapper {

@Override
public boolean supports(QuestionType questionType) {
return questionType == QuestionType.CHECKBOX;
}

@Override
public NewCheckboxAnswer mapToAnswer(ReviewAnswerRequest answerRequest) {
if (answerRequest.text() != null) {
throw new CheckBoxAnswerIncludedTextException(answerRequest.questionId());
}
return new NewCheckboxAnswer(answerRequest.questionId(), answerRequest.selectedOptionIds());
}
}
Loading

0 comments on commit 722c603

Please sign in to comment.