Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: 스터디 공지 목록 조회 API 추가 #644

Merged
merged 4 commits into from
Aug 20, 2024
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 @@ -353,4 +353,8 @@ public boolean isAdmin() {
public boolean isMentor() {
return studyRole.equals(MENTOR);
}

public boolean isStudent() {
return studyRole.equals(STUDENT);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,10 @@

import com.gdschongik.gdsc.domain.study.application.CommonStudyService;
import com.gdschongik.gdsc.domain.study.dto.response.CommonStudyResponse;
import com.gdschongik.gdsc.domain.study.dto.response.StudyAnnouncementResponse;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import java.util.List;
import lombok.RequiredArgsConstructor;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
Expand All @@ -25,4 +27,11 @@ public ResponseEntity<CommonStudyResponse> getStudyInformation(@PathVariable Lon
CommonStudyResponse response = commonStudyService.getStudyInformation(studyId);
return ResponseEntity.ok(response);
}

@Operation(summary = "스터디 공지 목록 조회", description = "스터디 공지 목록을 조회합니다.")
@GetMapping("/{studyId}/announcements")
public ResponseEntity<List<StudyAnnouncementResponse>> getStudyAnnouncements(@PathVariable Long studyId) {
List<StudyAnnouncementResponse> response = commonStudyService.getStudyAnnouncements(studyId);
return ResponseEntity.ok(response);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,20 @@

import static com.gdschongik.gdsc.global.exception.ErrorCode.STUDY_NOT_FOUND;

import com.gdschongik.gdsc.domain.member.domain.Member;
import com.gdschongik.gdsc.domain.study.dao.StudyAnnouncementRepository;
import com.gdschongik.gdsc.domain.study.dao.StudyHistoryRepository;
import com.gdschongik.gdsc.domain.study.dao.StudyRepository;
import com.gdschongik.gdsc.domain.study.domain.Study;
import com.gdschongik.gdsc.domain.study.domain.StudyAnnouncement;
import com.gdschongik.gdsc.domain.study.domain.StudyHistory;
import com.gdschongik.gdsc.domain.study.domain.StudyValidator;
import com.gdschongik.gdsc.domain.study.dto.response.CommonStudyResponse;
import com.gdschongik.gdsc.domain.study.dto.response.StudyAnnouncementResponse;
import com.gdschongik.gdsc.global.exception.CustomException;
import com.gdschongik.gdsc.global.util.MemberUtil;
import java.util.List;
import java.util.Optional;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
Expand All @@ -17,10 +27,28 @@
public class CommonStudyService {

private final StudyRepository studyRepository;
private final StudyHistoryRepository studyHistoryRepository;
private final StudyAnnouncementRepository studyAnnouncementRepository;
private final MemberUtil memberUtil;
private final StudyValidator studyValidator;

@Transactional(readOnly = true)
public CommonStudyResponse getStudyInformation(Long studyId) {
Study study = studyRepository.findById(studyId).orElseThrow(() -> new CustomException(STUDY_NOT_FOUND));
return CommonStudyResponse.from(study);
}

@Transactional(readOnly = true)
public List<StudyAnnouncementResponse> getStudyAnnouncements(Long studyId) {
Member currentMember = memberUtil.getCurrentMember();
final Study study = studyRepository.findById(studyId).orElseThrow(() -> new CustomException(STUDY_NOT_FOUND));
Optional<StudyHistory> studyHistory = studyHistoryRepository.findByMenteeAndStudyId(currentMember, studyId);

studyValidator.validateStudyMentorOrStudent(currentMember, study, studyHistory);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

음 공지목록 조회는 굳이 권한 검증이 필요한가... 싶은데
이미 구현을 해두셔서 제거하기엔 좀 그렇고... 한번 보시고 제거 검토해보시면 좋을듯 합니다


final List<StudyAnnouncement> studyAnnouncements =
studyAnnouncementRepository.findAllByStudyIdOrderByCreatedAtDesc(studyId);

return studyAnnouncements.stream().map(StudyAnnouncementResponse::from).toList();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,14 @@

import com.gdschongik.gdsc.domain.study.domain.StudyAnnouncement;
import com.gdschongik.gdsc.global.exception.CustomException;
import java.util.List;
import org.springframework.data.jpa.repository.JpaRepository;

public interface StudyAnnouncementRepository extends JpaRepository<StudyAnnouncement, Long> {

default StudyAnnouncement getById(Long id) {
return findById(id).orElseThrow(() -> new CustomException(STUDY_ANNOUNCEMENT_NOT_FOUND));
}

List<StudyAnnouncement> findAllByStudyIdOrderByCreatedAtDesc(Long studyId);
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import com.gdschongik.gdsc.domain.member.domain.Member;
import com.gdschongik.gdsc.global.annotation.DomainService;
import com.gdschongik.gdsc.global.exception.CustomException;
import java.util.Optional;

@DomainService
public class StudyValidator {
Expand All @@ -14,7 +15,7 @@ public void validateStudyMentor(Member currentMember, Study study) {
return;
}

// 어드민이 아니고 멘토 역할도 아니면 예외가 밸생합니다.
// 멘토인지 검증
if (!currentMember.isMentor()) {
throw new CustomException(STUDY_ACCESS_NOT_ALLOWED);
}
Expand All @@ -24,4 +25,21 @@ public void validateStudyMentor(Member currentMember, Study study) {
throw new CustomException(STUDY_MENTOR_INVALID);
}
}

public void validateStudyMentorOrStudent(Member currentMember, Study study, Optional<StudyHistory> studyHistory) {
// 어드민인 경우 검증 통과
if (currentMember.isAdmin()) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

중복 로직인데 위에 있는 메서드 그대로 호출해서 쓰면 될 것 같습니다

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

음 근데 아예 호출하면 에러터지니 따로 private 메소드로 빼서 사용하도록할게여

return;
}

// 해당 스터디의 수강생인지 검증
if (currentMember.isStudent() && studyHistory.isEmpty()) {
throw new CustomException(STUDY_ACCESS_NOT_ALLOWED);
}

// 해당 스터디의 담당 멘토인지 검증
if (!currentMember.getId().equals(study.getMentor().getId())) {
throw new CustomException(STUDY_MENTOR_INVALID);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package com.gdschongik.gdsc.domain.study.dto.response;

import com.gdschongik.gdsc.domain.study.domain.StudyAnnouncement;
import io.swagger.v3.oas.annotations.media.Schema;
import java.time.LocalDate;

public record StudyAnnouncementResponse(
Long studyAnnounceId,
@Schema(description = "제목") String title,
@Schema(description = "링크") String link,
@Schema(description = "생성 일자") LocalDate createdDate) {

public static StudyAnnouncementResponse from(StudyAnnouncement studyAnnouncement) {
return new StudyAnnouncementResponse(
studyAnnouncement.getId(),
studyAnnouncement.getTitle(),
studyAnnouncement.getLink(),
studyAnnouncement.getCreatedAt().toLocalDate());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
import com.gdschongik.gdsc.global.exception.CustomException;
import com.gdschongik.gdsc.helper.FixtureHelper;
import java.time.LocalDateTime;
import java.util.Optional;
import org.junit.jupiter.api.Nested;
import org.junit.jupiter.api.Test;

Expand All @@ -30,7 +31,7 @@ private Member createAdmin(Long id) {
}

@Nested
class 스터디_검증시 {
class 스터디_멘토역할_검증시 {

@Test
void 멘토역할이_아니라면_실패한다() {
Expand Down Expand Up @@ -88,4 +89,45 @@ class 스터디_검증시 {
.doesNotThrowAnyException();
}
}

@Nested
class 스터디_멘토_또는_학생역할_검증시 {

@Test
void 수강하지않는_스터디가_아니라면_실패한다() {
// given
Member student = createMember(1L);
Member mentor = createMentor(2L);
LocalDateTime assignmentCreatedDate = LocalDateTime.now().minusDays(1);
Study study = fixtureHelper.createStudy(
mentor,
Period.createPeriod(assignmentCreatedDate.plusDays(5), assignmentCreatedDate.plusDays(10)),
Period.createPeriod(assignmentCreatedDate.minusDays(5), assignmentCreatedDate));
StudyHistory studyHistory = null;

// when & then
assertThatThrownBy(() -> studyValidator.validateStudyMentorOrStudent(
student, study, Optional.ofNullable(studyHistory)))
.isInstanceOf(CustomException.class)
.hasMessage(STUDY_ACCESS_NOT_ALLOWED.getMessage());
}

@Test
void 멘토이지만_자신이_맡은_스터디가_아니라면_실패한다() {
// given
Member currentMember = createMentor(1L);
Member mentor = createMentor(2L);
LocalDateTime assignmentCreatedDate = LocalDateTime.now().minusDays(1);
Study study = fixtureHelper.createStudy(
mentor,
Period.createPeriod(assignmentCreatedDate.plusDays(5), assignmentCreatedDate.plusDays(10)),
Period.createPeriod(assignmentCreatedDate.minusDays(5), assignmentCreatedDate));

// when & then
assertThatThrownBy(
() -> studyValidator.validateStudyMentorOrStudent(currentMember, study, Optional.empty()))
.isInstanceOf(CustomException.class)
.hasMessage(STUDY_MENTOR_INVALID.getMessage());
}
}
}