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
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ public class ReviewController {
private final ReviewService reviewService;

@GetMapping("/mentorings/{mentoringId}/reviews")
@Operation(summary = "멘토링 리뷰 조회", description = "멘토링 리뷰 목록을 조회합니다.")
@Operation(summary = "멘토링 리뷰 목록 조회", description = "멘토링 리뷰 목록을 조회합니다.")
public RsData<ReviewPagingResponse> getReviews(
@PathVariable Long mentoringId,
@RequestParam(defaultValue = "0") int page,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ public record MentoringDetailDto(
String bio,
@Schema(description = "멘토링 썸네일")
String thumb,
@Schema(description = "멘토링 평점")
Double rating,
@Schema(description = "생성일")
LocalDateTime createDate,
@Schema(description = "수정일")
Expand All @@ -29,6 +31,7 @@ public static MentoringDetailDto from(Mentoring mentoring) {
mentoring.getTagNames(),
mentoring.getBio(),
mentoring.getThumb(),
mentoring.getRating(),
mentoring.getCreateDate(),
mentoring.getModifyDate()
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,18 +7,18 @@
import java.util.List;

public record MentoringRequest(
@Schema(description = "멘토링 제목")
@Schema(description = "멘토링 제목", example = "title")
@NotNull @Size(max = 100)
String title,

@Schema(description = "멘토링 태그", example = "[\"Java\", \"Spring\"]")
List<String> tags,

@Schema(description = "멘토링 소개")
@Schema(description = "멘토링 소개", example = "bio")
@NotNull
String bio,

@Schema(description = "멘토링 썸네일")
@Schema(description = "멘토링 썸네일", example = "test.png")
String thumb
) {
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ public record ReviewRequest(
Double rating,

@Size(max = 1000)
@Schema(description = "리뷰 내용")
@Schema(description = "리뷰 내용", example = "review content")
String content
) {
}
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,9 @@ public class Mentoring extends BaseEntity {
@Column(length = 255)
private String thumb;

@Column
private double rating = 0.0;

@Builder
public Mentoring(Mentor mentor, String title, String bio, String thumb) {
this.mentor = mentor;
Expand All @@ -56,6 +59,10 @@ public void updateTags(List<Tag> tags) {
}
}

public void updateRating(double averageRating) {
this.rating = averageRating;
}

public boolean isOwner(Mentor mentor) {
return this.mentor.equals(mentor);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ public enum MentoringErrorCode implements ErrorCode {
NOT_FOUND_MENTEE("404-3", "멘티를 찾을 수 없습니다."),

// 409
ALREADY_EXISTS_MENTORING("409-1", "이미 멘토링 정보가 존재합니다.");
ALREADY_EXISTS_MENTORING("409-1", "이미 동일한 이름의 멘토링이 존재합니다.");

private final String code;
private final String message;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,5 +9,6 @@
public interface MentoringRepository extends JpaRepository<Mentoring, Long>, MentoringRepositoryCustom {
List<Mentoring> findByMentorId(Long mentorId);
Optional<Mentoring> findTopByOrderByIdDesc();
boolean existsByMentorId(Long mentorId);
boolean existsByMentorIdAndTitle(Long mentorId, String title);
boolean existsByMentorIdAndTitleAndIdNot(Long mentorId, String title, Long MentoringId);
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
package com.back.domain.mentoring.mentoring.repository;

import com.back.domain.member.mentor.entity.Mentor;
import com.back.domain.mentoring.mentoring.entity.Review;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
Expand All @@ -9,16 +8,26 @@
import org.springframework.data.repository.query.Param;

public interface ReviewRepository extends JpaRepository<Review, Long> {
boolean existsByReservationId(Long id);
boolean existsByReservationId(Long reservationId);

@Query("""
SELECT AVG(r.rating)
SELECT ROUND(AVG(r.rating), 1)
FROM Review r
INNER JOIN r.reservation res
WHERE res.mentor = :mentor
WHERE res.mentor.id = :mentorId
""")
Double findAverageRating(
@Param("mentor") Mentor mentor
Double calculateMentorAverageRating(
@Param("mentorId") Long mentorId
);

@Query("""
SELECT ROUND(AVG(r.rating), 1)
FROM Review r
INNER JOIN r.reservation res
WHERE res.mentoring.id = :mentoringId
""")
Double calculateMentoringAverageRating(
@Param("mentoringId") Long mentoringId
);

@Query("""
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,10 +51,7 @@ public MentoringResponse getMentoring(Long mentoringId) {

@Transactional
public MentoringResponse createMentoring(MentoringRequest reqDto, Mentor mentor) {
// 멘토당 멘토링 1개 제한 체크 (추후 1:N 변경 시 제거 필요)
if (mentoringRepository.existsByMentorId(mentor.getId())) {
throw new ServiceException(MentoringErrorCode.ALREADY_EXISTS_MENTORING);
}
validateMentoringTitle(mentor.getId(), reqDto.title());

Mentoring mentoring = Mentoring.builder()
.mentor(mentor)
Expand All @@ -79,6 +76,7 @@ public MentoringResponse updateMentoring(Long mentoringId, MentoringRequest reqD
Mentoring mentoring = mentoringStorage.findMentoring(mentoringId);

validateOwner(mentoring, mentor);
validateMentoringTitleForUpdate(mentor.getId(), reqDto.title(), mentoringId);

List<Tag> tags = getOrCreateTags(reqDto.tags());

Expand All @@ -100,11 +98,6 @@ public void deleteMentoring(Long mentoringId, Mentor mentor) {
if (mentoringStorage.hasReservationsForMentoring(mentoring.getId())) {
throw new ServiceException(MentoringErrorCode.CANNOT_DELETE_MENTORING);
}

// 멘토 슬롯 있을 시 일괄 삭제 (추후 1:N 변경 시 제거 필요)
if (mentoringStorage.hasMentorSlotsForMentor(mentor.getId())) {
mentoringStorage.deleteMentorSlotsData(mentor.getId());
}
mentoringRepository.delete(mentoring);
}

Expand Down Expand Up @@ -153,4 +146,16 @@ private void validateOwner(Mentoring mentoring, Mentor mentor) {
throw new ServiceException(MentoringErrorCode.FORBIDDEN_NOT_OWNER);
}
}

private void validateMentoringTitle(Long mentorId, String title) {
if (mentoringRepository.existsByMentorIdAndTitle(mentorId, title)) {
throw new ServiceException(MentoringErrorCode.ALREADY_EXISTS_MENTORING);
}
}

private void validateMentoringTitleForUpdate(Long mentorId, String title, Long mentoringId) {
if (mentoringRepository.existsByMentorIdAndTitleAndIdNot(mentorId, title, mentoringId)) {
throw new ServiceException(MentoringErrorCode.ALREADY_EXISTS_MENTORING);
}
}
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
package com.back.domain.mentoring.mentoring.service;

import com.back.domain.member.mentor.entity.Mentor;
import com.back.domain.mentoring.mentoring.entity.Mentoring;
import com.back.domain.mentoring.mentoring.error.MentoringErrorCode;
import com.back.domain.mentoring.mentoring.repository.MentoringRepository;
Expand Down Expand Up @@ -36,11 +35,6 @@ public Mentoring findMentoring(Long mentoringId) {
.orElseThrow(() -> new ServiceException(MentoringErrorCode.NOT_FOUND_MENTORING));
}

// TODO : 멘토:멘토링 1:N으로 변경 시 삭제 예정
public Mentoring findMentoringByMentor(Mentor mentor) {
return findMentoringsByMentorId(mentor.getId()).getFirst();
}

public List<Mentoring> findMentoringsByMentorId(Long mentorId) {
List<Mentoring> mentorings = mentoringRepository.findByMentorId(mentorId);
if (mentorings.isEmpty()) {
Expand Down Expand Up @@ -73,11 +67,4 @@ public boolean hasMentorSlotsForMentor(Long mentorId) {
public boolean hasReservationForMentorSlot(Long slotId) {
return reservationRepository.existsByMentorSlotId(slotId);
}


// ===== 데이터 조작 메서드 =====

public void deleteMentorSlotsData(Long mentorId) {
mentorSlotRepository.deleteAllByMentorId(mentorId);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import com.back.domain.member.mentor.entity.Mentor;
import com.back.domain.mentoring.mentoring.dto.request.ReviewRequest;
import com.back.domain.mentoring.mentoring.dto.response.ReviewResponse;
import com.back.domain.mentoring.mentoring.entity.Mentoring;
import com.back.domain.mentoring.mentoring.entity.Review;
import com.back.domain.mentoring.mentoring.error.ReviewErrorCode;
import com.back.domain.mentoring.mentoring.repository.ReviewRepository;
Expand Down Expand Up @@ -54,6 +55,7 @@ public ReviewResponse createReview(Long reservationId, ReviewRequest reqDto, Men
.build();
reviewRepository.save(review);

updateMentoringRating(review.getReservation().getMentoring());
updateMentorRating(reservation.getMentor());

return ReviewResponse.from(review);
Expand All @@ -67,31 +69,36 @@ public ReviewResponse updateReview(Long reviewId, ReviewRequest reqDto, Mentee m
validateRating(reqDto.rating());

review.update(reqDto.rating(), reqDto.content());
updateMentoringRating(review.getReservation().getMentoring());
updateMentorRating(review.getReservation().getMentor());

return ReviewResponse.from(review);
}

@Transactional
public ReviewResponse deleteReview(Long reviewId, Mentee mentee) {
public void deleteReview(Long reviewId, Mentee mentee) {
Review review = findReview(reviewId);

validateMentee(mentee, review);

reviewRepository.delete(review);
updateMentoringRating(review.getReservation().getMentoring());
updateMentorRating(review.getReservation().getMentor());

return ReviewResponse.from(review);
}


// ===== 평점 업데이트 =====

private void updateMentorRating(Mentor mentor) {
Double averageRating = reviewRepository.findAverageRating(mentor);
Double averageRating = reviewRepository.calculateMentorAverageRating(mentor.getId());
mentor.updateRating(averageRating != null ? averageRating : 0.0);
}

private void updateMentoringRating(Mentoring mentoring) {
Double averageRating = reviewRepository.calculateMentoringAverageRating (mentoring.getId());
mentoring.updateRating(averageRating != null ? averageRating : 0.0);
}


// ===== 헬퍼 메서드 =====

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ public record ReservationRequest(
@NotNull
Long mentoringId,

@Schema(description = "사전 질문")
@Schema(description = "사전 질문", example = "question")
String preQuestion
) {
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,24 +11,24 @@
import java.util.List;

public record MentorSlotRepetitionRequest(
@Schema(description = "반복 시작일")
@Schema(description = "반복 시작일", example = "yyyy-MM-dd")
@NotNull
LocalDate repeatStartDate,

@Schema(description = "반복 종료일")
@Schema(description = "반복 종료일", example = "yyyy-MM-dd")
@NotNull
LocalDate repeatEndDate,

@Schema(description = "반복 요일")
@Schema(description = "반복 요일", example = "[\"MONDAY\", \"FRIDAY\"]")
@NotEmpty
List<DayOfWeek> daysOfWeek,

@Schema(description = "시작 시간")
@Schema(description = "시작 시간", example = "HH:mm:ss")
@NotNull
@JsonFormat(pattern = "HH:mm:ss")
LocalTime startTime,

@Schema(description = "종료 시간")
@Schema(description = "종료 시간", example = "HH:mm:ss")
@NotNull
@JsonFormat(pattern = "HH:mm:ss")
LocalTime endTime
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,11 @@ public record MentorSlotRequest(
@NotNull
Long mentorId,

@Schema(description = "시작 일시")
@Schema(description = "시작 일시", example = "yyyy-MM-ddTHH:mm:ss")
@NotNull
LocalDateTime startDateTime,

@Schema(description = "종료 일시")
@Schema(description = "종료 일시", example = "yyyy-MM-ddTHH:mm:ss")
@NotNull
LocalDateTime endDateTime
) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,16 +6,20 @@
import com.back.domain.mentoring.slot.dto.MentorSlotDetailDto;
import com.back.domain.mentoring.slot.entity.MentorSlot;

import java.util.List;

public record MentorSlotResponse(
MentorSlotDetailDto mentorSlot,
MentorDto mentor,
MentoringDto mentoring
List<MentoringDto> mentorings
) {
public static MentorSlotResponse from(MentorSlot mentorSlot, Mentoring mentoring) {
public static MentorSlotResponse from(MentorSlot mentorSlot, List<Mentoring> mentorings) {
return new MentorSlotResponse(
MentorSlotDetailDto.from(mentorSlot),
MentorDto.from(mentorSlot.getMentor()),
MentoringDto.from(mentoring)
mentorings.stream()
.map(MentoringDto::from)
.toList()
);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -54,14 +54,14 @@ public List<MentorSlotDto> getAvailableMentorSlots(Long mentorId, LocalDateTime
@Transactional(readOnly = true)
public MentorSlotResponse getMentorSlot(Long slotId) {
MentorSlot mentorSlot = mentorStorage.findMentorSlot(slotId);
Mentoring mentoring = mentorStorage.findMentoringByMentor(mentorSlot.getMentor());
List<Mentoring> mentorings = mentorStorage.findMentoringsByMentorId(mentorSlot.getMentor().getId());

return MentorSlotResponse.from(mentorSlot, mentoring);
return MentorSlotResponse.from(mentorSlot, mentorings);
}

@Transactional
public MentorSlotResponse createMentorSlot(MentorSlotRequest reqDto, Mentor mentor) {
Mentoring mentoring = mentorStorage.findMentoringByMentor(mentor);
List<Mentoring> mentorings = mentorStorage.findMentoringsByMentorId(mentor.getId());

DateTimeValidator.validateTimeSlot(reqDto.startDateTime(), reqDto.endDateTime());
validateOverlappingSlots(mentor, reqDto.startDateTime(), reqDto.endDateTime());
Expand All @@ -73,7 +73,7 @@ public MentorSlotResponse createMentorSlot(MentorSlotRequest reqDto, Mentor ment
.build();
mentorSlotRepository.save(mentorSlot);

return MentorSlotResponse.from(mentorSlot, mentoring);
return MentorSlotResponse.from(mentorSlot, mentorings);
}

@Transactional
Expand All @@ -92,7 +92,6 @@ public void createMentorSlotRepetition(MentorSlotRepetitionRequest reqDto, Mento

@Transactional
public MentorSlotResponse updateMentorSlot(Long slotId, MentorSlotRequest reqDto, Mentor mentor) {
Mentoring mentoring = mentorStorage.findMentoringByMentor(mentor);
MentorSlot mentorSlot = mentorStorage.findMentorSlot(slotId);

validateOwner(mentorSlot, mentor);
Expand All @@ -104,7 +103,8 @@ public MentorSlotResponse updateMentorSlot(Long slotId, MentorSlotRequest reqDto

mentorSlot.updateTime(reqDto.startDateTime(), reqDto.endDateTime());

return MentorSlotResponse.from(mentorSlot, mentoring);
List<Mentoring> mentorings = mentorStorage.findMentoringsByMentorId(mentor.getId());
return MentorSlotResponse.from(mentorSlot, mentorings);
}

@Transactional
Expand Down
Loading