From 75cb83a099760ee43676e7831c207b135550db6a Mon Sep 17 00:00:00 2001 From: KyungMin Lee Date: Wed, 28 Feb 2024 14:16:17 +0900 Subject: [PATCH] =?UTF-8?q?Feat/#141=20=EB=A6=AC=EB=B7=B0=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80=20=EA=B8=B0=EB=8A=A5=20=EA=B5=AC=ED=98=84=20(#190)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * [FEAT] Review 도메인 구현 * [FEAT] Review Exception 구현 * [FEAT] Review Repository 구현 * [FEAT] Review 생성 서비스 기능 구현 * [FEAT] 잔디력 증감 알고리즘 구현 * [FIX] 메소드명 변경에 따른 빌드 실패 오류 수정 * [RENAME] 잔디력 로직 생성에 따른 기본 값 변경 --- .../domain/project/domain/entity/Project.java | 2 +- .../review/application/ReviewService.java | 56 +++++++++++++ .../application/dto/req/ReviewCreateReq.java | 14 ++++ .../application/dto/res/ReviewCreateRes.java | 30 +++++++ .../mogakgo/domain/review/domain/Review.java | 81 +++++++++++++++++++ .../review/domain/enums/ReviewRating.java | 18 +++++ .../review/exception/ReviewException.java | 11 +++ .../infrastructure/ReviewJpaRepository.java | 14 ++++ .../oeid/mogakgo/domain/user/domain/User.java | 27 ++++--- .../mogakgo/exception/code/ErrorCode400.java | 5 ++ 10 files changed, 247 insertions(+), 11 deletions(-) create mode 100644 src/main/java/io/oeid/mogakgo/domain/review/application/ReviewService.java create mode 100644 src/main/java/io/oeid/mogakgo/domain/review/application/dto/req/ReviewCreateReq.java create mode 100644 src/main/java/io/oeid/mogakgo/domain/review/application/dto/res/ReviewCreateRes.java create mode 100644 src/main/java/io/oeid/mogakgo/domain/review/domain/Review.java create mode 100644 src/main/java/io/oeid/mogakgo/domain/review/domain/enums/ReviewRating.java create mode 100644 src/main/java/io/oeid/mogakgo/domain/review/exception/ReviewException.java create mode 100644 src/main/java/io/oeid/mogakgo/domain/review/infrastructure/ReviewJpaRepository.java diff --git a/src/main/java/io/oeid/mogakgo/domain/project/domain/entity/Project.java b/src/main/java/io/oeid/mogakgo/domain/project/domain/entity/Project.java index 0fa3b11e..f940fc51 100644 --- a/src/main/java/io/oeid/mogakgo/domain/project/domain/entity/Project.java +++ b/src/main/java/io/oeid/mogakgo/domain/project/domain/entity/Project.java @@ -101,7 +101,7 @@ public void cancel(User tokenUser, boolean projectHasReq) { validateAvailableCancel(tokenUser); // 매칭 준비중이지만 요청이 있을때는 잔디력 감소 if (projectHasReq) { - this.creator.decreaseJandiRate(); + this.creator.updateJandiRateByCancel(); } this.projectStatus = ProjectStatus.CANCELED; } diff --git a/src/main/java/io/oeid/mogakgo/domain/review/application/ReviewService.java b/src/main/java/io/oeid/mogakgo/domain/review/application/ReviewService.java new file mode 100644 index 00000000..48e1ab90 --- /dev/null +++ b/src/main/java/io/oeid/mogakgo/domain/review/application/ReviewService.java @@ -0,0 +1,56 @@ +package io.oeid.mogakgo.domain.review.application; + +import io.oeid.mogakgo.domain.project.infrastructure.ProjectJpaRepository; +import io.oeid.mogakgo.domain.review.application.dto.req.ReviewCreateReq; +import io.oeid.mogakgo.domain.review.application.dto.res.ReviewCreateRes; +import io.oeid.mogakgo.domain.review.domain.Review; +import io.oeid.mogakgo.domain.review.exception.ReviewException; +import io.oeid.mogakgo.domain.review.infrastructure.ReviewJpaRepository; +import io.oeid.mogakgo.domain.user.application.UserCommonService; +import io.oeid.mogakgo.exception.code.ErrorCode400; +import io.oeid.mogakgo.exception.code.ErrorCode404; +import java.time.Duration; +import java.time.LocalDateTime; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +@Service +@RequiredArgsConstructor +public class ReviewService { + + private final ReviewJpaRepository reviewRepository; + private final ProjectJpaRepository projectRepository; + private final UserCommonService userCommonService; + + @Transactional + public ReviewCreateRes createNewReview(ReviewCreateReq request) { + reviewRepository.findReviewByProjectData(request.getSenderId(), request.getReceiverId(), + request.getProjectId()).ifPresent(review -> { + throw new ReviewException(ErrorCode400.REVIEW_ALREADY_EXISTS); + }); + var sender = userCommonService.getUserById(request.getSenderId()); + var receiver = userCommonService.getUserById(request.getReceiverId()); + var project = projectRepository.findById(request.getProjectId()) + .orElseThrow(() -> new ReviewException(ErrorCode404.PROJECT_NOT_FOUND)); + var review = reviewRepository.save(Review.builder() + .sender(sender) + .receiver(receiver) + .project(project) + .rating(request.getRating()) + .build() + ); + receiver.updateJandiRateByReview(review.getRating(), + calculateProjectTime(project.getMeetingInfo().getMeetStartTime(), + project.getMeetingInfo().getMeetEndTime())); + return ReviewCreateRes.from(review); + } + + private double calculateProjectTime(LocalDateTime meetStartTime, LocalDateTime meetEndTime) { + Duration duration = Duration.between(meetStartTime, meetEndTime); + double hours = duration.toHours(); + double minutes = duration.toMinutes() % 60; + return hours + minutes / 60; + } + +} diff --git a/src/main/java/io/oeid/mogakgo/domain/review/application/dto/req/ReviewCreateReq.java b/src/main/java/io/oeid/mogakgo/domain/review/application/dto/req/ReviewCreateReq.java new file mode 100644 index 00000000..2aa0ba1f --- /dev/null +++ b/src/main/java/io/oeid/mogakgo/domain/review/application/dto/req/ReviewCreateReq.java @@ -0,0 +1,14 @@ +package io.oeid.mogakgo.domain.review.application.dto.req; + +import io.oeid.mogakgo.domain.review.domain.enums.ReviewRating; +import lombok.AllArgsConstructor; +import lombok.Getter; + +@Getter +@AllArgsConstructor +public class ReviewCreateReq { + private Long senderId; + private Long receiverId; + private Long projectId; + private ReviewRating rating; +} diff --git a/src/main/java/io/oeid/mogakgo/domain/review/application/dto/res/ReviewCreateRes.java b/src/main/java/io/oeid/mogakgo/domain/review/application/dto/res/ReviewCreateRes.java new file mode 100644 index 00000000..6f14a1e9 --- /dev/null +++ b/src/main/java/io/oeid/mogakgo/domain/review/application/dto/res/ReviewCreateRes.java @@ -0,0 +1,30 @@ +package io.oeid.mogakgo.domain.review.application.dto.res; + +import io.oeid.mogakgo.domain.review.domain.Review; +import io.oeid.mogakgo.domain.review.domain.enums.ReviewRating; +import java.time.LocalDateTime; +import lombok.AccessLevel; +import lombok.AllArgsConstructor; +import lombok.Getter; + +@Getter +@AllArgsConstructor(access = AccessLevel.PRIVATE) +public class ReviewCreateRes { + private Long id; + private Long senderId; + private Long receiverId; + private Long projectId; + private ReviewRating rating; + private LocalDateTime createdAt; + + public static ReviewCreateRes from(Review review) { + return new ReviewCreateRes( + review.getId(), + review.getSender().getId(), + review.getReceiver().getId(), + review.getProject().getId(), + review.getRating(), + review.getCreatedAt() + ); + } +} diff --git a/src/main/java/io/oeid/mogakgo/domain/review/domain/Review.java b/src/main/java/io/oeid/mogakgo/domain/review/domain/Review.java new file mode 100644 index 00000000..7c9d9719 --- /dev/null +++ b/src/main/java/io/oeid/mogakgo/domain/review/domain/Review.java @@ -0,0 +1,81 @@ +package io.oeid.mogakgo.domain.review.domain; + +import static jakarta.persistence.GenerationType.IDENTITY; +import static lombok.AccessLevel.PROTECTED; + +import io.oeid.mogakgo.domain.project.domain.entity.Project; +import io.oeid.mogakgo.domain.review.domain.enums.ReviewRating; +import io.oeid.mogakgo.domain.review.exception.ReviewException; +import io.oeid.mogakgo.domain.user.domain.User; +import io.oeid.mogakgo.exception.code.ErrorCode400; +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.EnumType; +import jakarta.persistence.Enumerated; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.Id; +import jakarta.persistence.JoinColumn; +import jakarta.persistence.ManyToOne; +import jakarta.persistence.Table; +import java.time.LocalDateTime; +import lombok.Builder; +import lombok.Getter; +import lombok.NoArgsConstructor; +import org.hibernate.annotations.CreationTimestamp; + +@Getter +@Entity +@Table(name = "review_tb") +@NoArgsConstructor(access = PROTECTED) +public class Review { + + @Id + @GeneratedValue(strategy = IDENTITY) + @Column(name = "id") + private Long id; + + @ManyToOne + @JoinColumn(name = "sender_id") + private User sender; + + @ManyToOne + @JoinColumn(name = "receiver_id") + private User receiver; + + @ManyToOne + @JoinColumn(name = "project_id") + private Project project; + + @Enumerated(EnumType.STRING) + @Column(name = "rating") + private ReviewRating rating; + + @CreationTimestamp + @Column(name = "created_at") + LocalDateTime createdAt; + + @Builder + private Review(User sender, User receiver, Project project, ReviewRating rating) { + validateUsers(sender, receiver); + this.sender = sender; + this.receiver = receiver; + this.project = validateProject(project); + this.rating = rating; + } + + private void validateUsers(User sender, User receiver) { + if (sender == null || receiver == null) { + throw new ReviewException(ErrorCode400.REVIEW_SENDER_OR_RECEIVER_NOT_FOUND); + } + if (sender.getId().equals(receiver.getId())) { + throw new ReviewException(ErrorCode400.REVIEW_USER_DUPLICATED); + } + } + + private Project validateProject(Project project) { + if (project == null) { + throw new ReviewException(ErrorCode400.REVIEW_PROJECT_NOT_NULL); + } + return project; + } +} diff --git a/src/main/java/io/oeid/mogakgo/domain/review/domain/enums/ReviewRating.java b/src/main/java/io/oeid/mogakgo/domain/review/domain/enums/ReviewRating.java new file mode 100644 index 00000000..29deeb42 --- /dev/null +++ b/src/main/java/io/oeid/mogakgo/domain/review/domain/enums/ReviewRating.java @@ -0,0 +1,18 @@ +package io.oeid.mogakgo.domain.review.domain.enums; + +import lombok.Getter; + +@Getter +public enum ReviewRating { + ONE(-2), + TWO(-1), + THREE(1), + FOUR(2), + FIVE(3); + + private final int value; + + ReviewRating(int value) { + this.value = value; + } +} diff --git a/src/main/java/io/oeid/mogakgo/domain/review/exception/ReviewException.java b/src/main/java/io/oeid/mogakgo/domain/review/exception/ReviewException.java new file mode 100644 index 00000000..9eba9500 --- /dev/null +++ b/src/main/java/io/oeid/mogakgo/domain/review/exception/ReviewException.java @@ -0,0 +1,11 @@ +package io.oeid.mogakgo.domain.review.exception; + +import io.oeid.mogakgo.exception.code.ErrorCode; +import io.oeid.mogakgo.exception.exception_class.CustomException; + +public class ReviewException extends CustomException { + + public ReviewException(ErrorCode errorCode) { + super(errorCode); + } +} diff --git a/src/main/java/io/oeid/mogakgo/domain/review/infrastructure/ReviewJpaRepository.java b/src/main/java/io/oeid/mogakgo/domain/review/infrastructure/ReviewJpaRepository.java new file mode 100644 index 00000000..f01b4769 --- /dev/null +++ b/src/main/java/io/oeid/mogakgo/domain/review/infrastructure/ReviewJpaRepository.java @@ -0,0 +1,14 @@ +package io.oeid.mogakgo.domain.review.infrastructure; + +import io.oeid.mogakgo.domain.review.domain.Review; +import java.util.Optional; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Query; +import org.springframework.stereotype.Repository; + +@Repository +public interface ReviewJpaRepository extends JpaRepository { + + @Query("select r from Review r where r.sender.id = ?1 and r.receiver.id = ?2 and r.project.id = ?3") + Optional findReviewByProjectData(Long id, Long id1, Long id2); +} \ No newline at end of file diff --git a/src/main/java/io/oeid/mogakgo/domain/user/domain/User.java b/src/main/java/io/oeid/mogakgo/domain/user/domain/User.java index 79503dbe..2ed682d9 100644 --- a/src/main/java/io/oeid/mogakgo/domain/user/domain/User.java +++ b/src/main/java/io/oeid/mogakgo/domain/user/domain/User.java @@ -5,6 +5,7 @@ import io.oeid.mogakgo.domain.achievement.domain.entity.Achievement; import io.oeid.mogakgo.domain.geo.domain.enums.Region; +import io.oeid.mogakgo.domain.review.domain.enums.ReviewRating; import io.oeid.mogakgo.domain.user.domain.enums.Role; import io.oeid.mogakgo.domain.user.exception.UserException; import io.oeid.mogakgo.exception.code.ErrorCode400; @@ -43,6 +44,7 @@ public class User { private static final int MAX_TAG_SIZE = 3; private static final int MAX_AVAILABLE_LIKE_COUNT = 10; + private static final double JANDI_WEIGHT = 2.5; @Id @GeneratedValue(strategy = GenerationType.IDENTITY) @@ -111,7 +113,8 @@ public class User { @JoinColumn(name = "achievement_id") private Achievement achievement; - private User(Long githubPk, String githubId, String avatarUrl, String githubUrl, String repositoryUrl) { + private User(Long githubPk, String githubId, String avatarUrl, String githubUrl, + String repositoryUrl) { this.githubPk = githubPk; this.username = githubId; this.githubId = githubId; @@ -119,11 +122,12 @@ private User(Long githubPk, String githubId, String avatarUrl, String githubUrl, this.githubUrl = githubUrl; this.repositoryUrl = repositoryUrl; this.role = Role.ROLE_USER; - this.jandiRate = 0d; + this.jandiRate = 10d; this.signupYn = false; } - public static User of(long githubPk, String username, String avatarUrl, String githubUrl, String repositoryUrl) { + public static User of(long githubPk, String username, String avatarUrl, String githubUrl, + String repositoryUrl) { return new User(githubPk, username, avatarUrl, githubUrl, repositoryUrl); } @@ -145,7 +149,8 @@ public Collection getAuthorities() { return List.of(new SimpleGrantedAuthority(role.name())); } - public void updateGithubInfo(String githubId, String avatarUrl, String githubUrl, String repositoryUrl) { + public void updateGithubInfo(String githubId, String avatarUrl, String githubUrl, + String repositoryUrl) { this.githubId = githubId; this.avatarUrl = avatarUrl; this.githubUrl = githubUrl; @@ -200,7 +205,8 @@ public void updateRegion(Region region) { } } - public void updateUserInfos(String username, String avatarUrl, String bio, Achievement achievement) { + public void updateUserInfos(String username, String avatarUrl, String bio, + Achievement achievement) { updateUsername(username); this.avatarUrl = verifyAvatarUrl(avatarUrl); this.bio = bio; @@ -208,9 +214,12 @@ public void updateUserInfos(String username, String avatarUrl, String bio, Achie deleteAllWantJobTags(); } - //TODO : 추후 구현 필요 - public void decreaseJandiRate() { - return; + public void updateJandiRateByReview(ReviewRating rating, double time) { + this.jandiRate += rating.getValue() * time * JANDI_WEIGHT; + } + + public void updateJandiRateByCancel() { + this.jandiRate += -5 * JANDI_WEIGHT; } private boolean validateAvailableRegionUpdate(Region region) { @@ -225,6 +234,4 @@ private String verifyAvatarUrl(String avatarUrl) { } - - } diff --git a/src/main/java/io/oeid/mogakgo/exception/code/ErrorCode400.java b/src/main/java/io/oeid/mogakgo/exception/code/ErrorCode400.java index 1ab1ae41..84fa77a6 100644 --- a/src/main/java/io/oeid/mogakgo/exception/code/ErrorCode400.java +++ b/src/main/java/io/oeid/mogakgo/exception/code/ErrorCode400.java @@ -56,6 +56,11 @@ public enum ErrorCode400 implements ErrorCode { CHAT_ROOM_CLOSED("E110101", "채팅방이 종료되어 채팅을 할 수 없습니다."), CHAT_ROOM_USER_CANNOT_DUPLICATE("E110102", "채팅방에 중복된 유저가 있습니다."), CHAT_ROOM_USER_NOT_CONTAINS("E110103", "채팅방에 해당 유저가 없습니다."), + + REVIEW_SENDER_OR_RECEIVER_NOT_FOUND("E120101", "리뷰를 작성하기 위한 유저 정보가 존재하지 않습니다."), + REVIEW_USER_DUPLICATED("E120102", "자신에 대한 리뷰는 작성할 수 없습니다."), + REVIEW_PROJECT_NOT_NULL("E120103", "리뷰를 작성하기 위한 프로젝트 정보가 존재하지 않습니다."), + REVIEW_ALREADY_EXISTS("E120104", "이미 작성된 리뷰가 존재합니다."), ; private final HttpStatus httpStatus = HttpStatus.BAD_REQUEST;