-
Notifications
You must be signed in to change notification settings - Fork 1
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 #642
Changes from 18 commits
fc8157d
07c10f7
05422cc
0bbf3f1
dfcd0d1
616f2f5
b10f888
15452f7
16c9aac
22d0afc
846ffa5
6593f4e
a6fb8db
5badce8
a444b60
868f05e
37d650b
fc65781
d944516
d878603
cffe5bb
55eea81
f20ef1a
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,20 +1,30 @@ | ||
package com.gdschongik.gdsc.domain.study.application; | ||
|
||
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.StudyDetailRepository; | ||
import com.gdschongik.gdsc.domain.study.dao.StudyHistoryRepository; | ||
import com.gdschongik.gdsc.domain.study.dao.StudyRepository; | ||
import com.gdschongik.gdsc.domain.study.domain.*; | ||
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.request.StudyAnnouncementCreateUpdateRequest; | ||
import com.gdschongik.gdsc.domain.study.dto.request.StudySessionCreateRequest; | ||
import com.gdschongik.gdsc.domain.study.dto.request.StudyUpdateRequest; | ||
import com.gdschongik.gdsc.domain.study.dto.response.MentorStudyResponse; | ||
import com.gdschongik.gdsc.domain.study.dto.response.StudyStudentResponse; | ||
import com.gdschongik.gdsc.global.exception.CustomException; | ||
import com.gdschongik.gdsc.global.exception.ErrorCode; | ||
import com.gdschongik.gdsc.global.util.MemberUtil; | ||
import java.util.ArrayList; | ||
import java.util.List; | ||
import java.util.Map; | ||
import java.util.function.Function; | ||
import java.util.stream.Collectors; | ||
import lombok.RequiredArgsConstructor; | ||
import lombok.extern.slf4j.Slf4j; | ||
import org.springframework.stereotype.Service; | ||
|
@@ -30,6 +40,8 @@ public class MentorStudyService { | |
private final StudyAnnouncementRepository studyAnnouncementRepository; | ||
private final StudyHistoryRepository studyHistoryRepository; | ||
private final StudyValidator studyValidator; | ||
private final StudyDetailRepository studyDetailRepository; | ||
private final StudyDetailValidator studyDetailValidator; | ||
|
||
@Transactional(readOnly = true) | ||
public List<MentorStudyResponse> getStudiesInCharge() { | ||
|
@@ -90,4 +102,39 @@ public void deleteStudyAnnouncement(Long studyAnnouncementId) { | |
|
||
log.info("[MentorStudyService] 스터디 공지 삭제 완료: studyAnnouncementId={}", studyAnnouncement.getId()); | ||
} | ||
|
||
// TODO session -> curriculum 변경 | ||
@Transactional | ||
public void updateStudy(Long studyId, StudyUpdateRequest request) { | ||
Member currentMember = memberUtil.getCurrentMember(); | ||
Study study = studyRepository.findById(studyId).orElseThrow(() -> new CustomException(STUDY_NOT_FOUND)); | ||
studyValidator.validateStudyMentor(currentMember, study); | ||
|
||
List<StudyDetail> studyDetails = studyDetailRepository.findAllByStudyId(studyId); | ||
studyDetailValidator.validateUpdateStudyDetail(studyDetails, request.studySessions()); | ||
|
||
study.update(request.notionLink(), request.introduction()); | ||
studyRepository.save(study); | ||
log.info("[MentorStudyService] 스터디 기본 정보 수정 완료: studyId={}", studyId); | ||
|
||
Map<Long, StudySessionCreateRequest> requestMap = request.studySessions().stream() | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 굳이 맵으로 만들 필요 없이 리스트에서 바로 찾아서 반환하면 될듯 합니다. |
||
.collect(Collectors.toMap(StudySessionCreateRequest::studyDetailId, Function.identity())); | ||
|
||
List<StudyDetail> updatedStudyDetails = new ArrayList<>(); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 굳이 updatedStudyDetails를 한번 더 담는 이유가 뭔가요? |
||
for (StudyDetail studyDetail : studyDetails) { | ||
Long id = studyDetail.getId(); | ||
StudySessionCreateRequest matchingSession = requestMap.get(id); | ||
|
||
studyDetail.updateSession( | ||
studyDetail.getStudy().getPeriod().getStartDate(), | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 세션 시작시간이 왜 항상 스터디 시작일이어야 하는 건가요? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 피그마상에서 세션 시작시간에 대해 요청을 받고 있지 않더라고요 |
||
matchingSession.title(), | ||
matchingSession.description(), | ||
matchingSession.difficulty(), | ||
matchingSession.status()); | ||
|
||
updatedStudyDetails.add(studyDetail); | ||
} | ||
studyDetailRepository.saveAll(updatedStudyDetails); | ||
log.info("[MentorStudyService] 스터디 상세정보 커리큘럼 작성 완료: studyDetailId={}", studyDetails); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change | ||||
---|---|---|---|---|---|---|
|
@@ -182,4 +182,9 @@ public boolean isStudyOngoing() { | |||||
public LocalDate getStartDate() { | ||||||
return period.getStartDate().toLocalDate(); | ||||||
} | ||||||
|
||||||
public void update(String link, String intro) { | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 위에 필드랑 겹쳐서 오류가 나서 intro로 만들었습니다 There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 그러면 this로 명시해서 넣으면 되지 않나요? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 아아 이전에 History쪽에서는 repository를 repo로 하신거 생각하고 이런식으로 처리를 하는건가보다~해서 intro라고 했습니다 다시 보니까 다 this로 바꿔져있군요! |
||||||
notionLink = link; | ||||||
introduction = intro; | ||||||
} | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 필드 업데이트에 대한 유효성 검토 필요
public void update(String link, String intro) {
if (link == null || link.isEmpty()) {
throw new IllegalArgumentException("Notion link cannot be null or empty");
}
if (intro == null || intro.isEmpty()) {
throw new IllegalArgumentException("Introduction cannot be null or empty");
}
notionLink = link;
introduction = intro;
} |
||||||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -4,9 +4,13 @@ | |
|
||
import com.gdschongik.gdsc.domain.member.domain.Member; | ||
import com.gdschongik.gdsc.domain.study.dto.request.AssignmentCreateUpdateRequest; | ||
import com.gdschongik.gdsc.domain.study.dto.request.StudySessionCreateRequest; | ||
import com.gdschongik.gdsc.global.annotation.DomainService; | ||
import com.gdschongik.gdsc.global.exception.CustomException; | ||
import java.time.LocalDateTime; | ||
import java.util.List; | ||
import java.util.Set; | ||
import java.util.stream.Collectors; | ||
|
||
@DomainService | ||
public class StudyDetailValidator { | ||
|
@@ -54,4 +58,23 @@ private void validateUpdateDeadline( | |
throw new CustomException(STUDY_DETAIL_ASSIGNMENT_INVALID_UPDATE_DEADLINE); | ||
} | ||
} | ||
|
||
public void validateUpdateStudyDetail(List<StudyDetail> studyDetails, List<StudySessionCreateRequest> requests) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. validator는 도메인 계층에 속한 도메인서비스이기 때문에, 외부에 대한 의존성을 가져서는 안됩니다. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 현재validator에서 만든 Set들을 서비스 레이어에서 만들어서넘기겠습니다 |
||
// StudyDetail 목록과 요청된 StudySessionCreateRequest 목록의 크기를 먼저 비교 | ||
if (studyDetails.size() != requests.size()) { | ||
throw new CustomException(STUDY_DETAIL_SESSION_SIZE_MISMATCH); | ||
} | ||
|
||
// StudyDetail ID를 추출하여 Set으로 저장 | ||
Set<Long> studyDetailIds = studyDetails.stream().map(StudyDetail::getId).collect(Collectors.toSet()); | ||
|
||
// 요청된 StudySessionCreateRequest의 StudyDetail ID를 추출하여 Set으로 저장 | ||
Set<Long> requestIds = | ||
requests.stream().map(StudySessionCreateRequest::studyDetailId).collect(Collectors.toSet()); | ||
|
||
// 두 ID 집합이 동일한지 비교하여 ID 불일치 시 예외를 던짐 | ||
if (!studyDetailIds.equals(requestIds)) { | ||
throw new CustomException(STUDY_DETAIL_ID_INVALID); | ||
} | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
package com.gdschongik.gdsc.domain.study.dto.request; | ||
|
||
import com.gdschongik.gdsc.domain.study.domain.Difficulty; | ||
import com.gdschongik.gdsc.domain.study.domain.StudyStatus; | ||
import io.swagger.v3.oas.annotations.media.Schema; | ||
import jakarta.validation.constraints.NotNull; | ||
|
||
public record StudySessionCreateRequest( | ||
@NotNull Long studyDetailId, | ||
@Schema(description = "제목") String title, | ||
@Schema(description = "설명") String description, | ||
@Schema(description = "난이도") Difficulty difficulty, | ||
@Schema(description = "휴강 여부") StudyStatus status) {} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
package com.gdschongik.gdsc.domain.study.dto.request; | ||
|
||
import io.swagger.v3.oas.annotations.media.Schema; | ||
import java.util.List; | ||
|
||
public record StudyUpdateRequest( | ||
@Schema(description = "스터디 소개 페이지 링크") String notionLink, | ||
@Schema(description = "스터디 한 줄 소개") String introduction, | ||
List<StudySessionCreateRequest> studySessions) {} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,79 @@ | ||
package com.gdschongik.gdsc.domain.study.application; | ||
|
||
import static com.gdschongik.gdsc.global.common.constant.StudyConstant.*; | ||
import static com.gdschongik.gdsc.global.common.constant.StudyConstant.SESSION_DESCRIPTION; | ||
import static org.assertj.core.api.Assertions.assertThat; | ||
|
||
import com.gdschongik.gdsc.domain.member.domain.Member; | ||
import com.gdschongik.gdsc.domain.member.domain.MemberRole; | ||
import com.gdschongik.gdsc.domain.recruitment.domain.vo.Period; | ||
import com.gdschongik.gdsc.domain.study.domain.Difficulty; | ||
import com.gdschongik.gdsc.domain.study.domain.Study; | ||
import com.gdschongik.gdsc.domain.study.domain.StudyDetail; | ||
import com.gdschongik.gdsc.domain.study.domain.StudyStatus; | ||
import com.gdschongik.gdsc.domain.study.dto.request.StudySessionCreateRequest; | ||
import com.gdschongik.gdsc.domain.study.dto.request.StudyUpdateRequest; | ||
import com.gdschongik.gdsc.helper.IntegrationTest; | ||
import java.time.LocalDateTime; | ||
import java.util.*; | ||
import org.junit.jupiter.api.*; | ||
import org.springframework.beans.factory.annotation.Autowired; | ||
|
||
public class MentorStudyServiceTest extends IntegrationTest { | ||
|
||
@Autowired | ||
private MentorStudyService mentorStudyService; | ||
|
||
@Nested | ||
class 스터디_정보_작성시 { | ||
|
||
@Test | ||
void 성공한다() { | ||
// given | ||
LocalDateTime now = LocalDateTime.now(); | ||
Member mentor = createMentor(); | ||
Study study = createNewStudy( | ||
mentor, | ||
4L, | ||
Period.createPeriod(now.plusDays(5), now.plusDays(10)), | ||
Period.createPeriod(now.minusDays(5), now)); | ||
for (int i = 1; i <= 4; i++) { | ||
Long week = (long) i; | ||
createNewStudyDetail(week, study, now, now.plusDays(7)); | ||
now = now.plusDays(8); | ||
} | ||
logoutAndReloginAs(study.getMentor().getId(), MemberRole.ASSOCIATE); | ||
|
||
List<StudySessionCreateRequest> sessionCreateRequests = new ArrayList<>(); | ||
for (int i = 1; i <= study.getTotalWeek(); i++) { | ||
Long id = (long) i; | ||
StudySessionCreateRequest sessionCreateRequest = new StudySessionCreateRequest( | ||
id, SESSION_TITLE + i, SESSION_DESCRIPTION + i, Difficulty.HIGH, StudyStatus.OPEN); | ||
sessionCreateRequests.add(sessionCreateRequest); | ||
} | ||
|
||
StudyUpdateRequest request = | ||
new StudyUpdateRequest(STUDY_NOTION_LINK, STUDY_INTRODUCTION, sessionCreateRequests); | ||
|
||
// when | ||
mentorStudyService.updateStudy(1L, request); | ||
|
||
// then | ||
Study savedStudy = studyRepository.findById(study.getId()).get(); | ||
assertThat(savedStudy.getNotionLink()).isEqualTo(request.notionLink()); | ||
assertThat(savedStudy.getIntroduction()).isEqualTo(request.introduction()); | ||
|
||
List<StudyDetail> studyDetails = studyDetailRepository.findAllByStudyId(1L); | ||
for (int i = 0; i < studyDetails.size(); i++) { | ||
StudyDetail studyDetail = studyDetails.get(i); | ||
Long expectedId = studyDetail.getId(); | ||
|
||
assertThat(studyDetail.getId()).isEqualTo(expectedId); | ||
assertThat(studyDetail.getSession().getTitle()).isEqualTo(SESSION_TITLE + expectedId); | ||
assertThat(studyDetail.getSession().getDescription()).isEqualTo(SESSION_DESCRIPTION + expectedId); | ||
assertThat(studyDetail.getSession().getDifficulty()).isEqualTo(Difficulty.HIGH); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 불필요하게 모든 필드를 전부 검증하지 않아도 됩니다. |
||
assertThat(studyDetail.getSession().getStatus()).isEqualTo(StudyStatus.OPEN); | ||
} | ||
} | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
두 로직을 추출하여 적절히 분리하는게 좋을 것 같습니다.