Skip to content

Commit

Permalink
feat: 과제 휴강 처리 API 추가 (#542)
Browse files Browse the repository at this point in the history
* feat: 스터디 휴강 처리 api 추가

* fix: 엔드포인트 수정

* fix: 에러코드 수정

* refactor: study를 외부에서 받아서 사용하도록 수정

* feat: 현재 멤버의 멘토 여부 검증 추가

* refactor: save 호출하도록 수정

* fix: 시큐리티 설정 추가

* remove: 클래스 레벨 트랜잭셔널 어노테이션 제거

* refactor: 현멤버의 멘토 여부 검증 로직을 메서드로 추출

* refactor: 스터디를 주입받도록 수정
  • Loading branch information
Sangwook02 authored Aug 2, 2024
1 parent 285e23f commit 6acc16c
Show file tree
Hide file tree
Showing 12 changed files with 229 additions and 2 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ public class StudyMentorController {
private final StudyMentorService studyMentorService;

@Operation(summary = "스터디 과제 개설", description = "멘토만 과제를 개설할 수 있습니다.")
@PutMapping("/assignment/{assignmentId}")
@PutMapping("/assignments/{assignmentId}")
public ResponseEntity<Void> createStudyAssignment(
@PathVariable Long assignmentId, @Valid @RequestBody AssignmentCreateRequest request) {
return null;
Expand All @@ -39,4 +39,11 @@ public ResponseEntity<AssignmentResponse> getStudyAssignment(@PathVariable Long
AssignmentResponse response = studyMentorService.getAssignment(studyDetailId);
return ResponseEntity.ok(response);
}

@Operation(summary = "스터디 과제 휴강 처리", description = "해당 주차 과제를 휴강 처리합니다.")
@PatchMapping("/assignments/{studyDetailId}/cancel")
public ResponseEntity<Void> cancelStudyAssignment(@PathVariable Long studyDetailId) {
studyMentorService.cancelStudyAssignment(studyDetailId);
return ResponseEntity.noContent().build();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,13 @@

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

import com.gdschongik.gdsc.domain.member.domain.Member;
import com.gdschongik.gdsc.domain.study.dao.StudyDetailRepository;
import com.gdschongik.gdsc.domain.study.domain.StudyDetail;
import com.gdschongik.gdsc.domain.study.domain.StudyDetailValidator;
import com.gdschongik.gdsc.domain.study.dto.response.AssignmentResponse;
import com.gdschongik.gdsc.global.exception.CustomException;
import com.gdschongik.gdsc.global.util.MemberUtil;
import java.util.List;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
Expand All @@ -17,7 +20,9 @@
@RequiredArgsConstructor
public class StudyMentorService {

private final MemberUtil memberUtil;
private final StudyDetailRepository studyDetailRepository;
private final StudyDetailValidator studyDetailValidator;

@Transactional(readOnly = true)
public List<AssignmentResponse> getWeeklyAssignments(Long studyId) {
Expand All @@ -32,4 +37,19 @@ public AssignmentResponse getAssignment(Long studyDetailId) {
.orElseThrow(() -> new CustomException(STUDY_DETAIL_NOT_FOUND));
return AssignmentResponse.from(studyDetail);
}

@Transactional
public void cancelStudyAssignment(Long studyDetailId) {
Member currentMember = memberUtil.getCurrentMember();
StudyDetail studyDetail = studyDetailRepository
.findById(studyDetailId)
.orElseThrow(() -> new CustomException(STUDY_DETAIL_NOT_FOUND));

studyDetailValidator.validateCancelStudyAssignment(currentMember, studyDetail);

studyDetail.cancelAssignment();
studyDetailRepository.save(studyDetail);

log.info("[StudyMentorService] 과제 휴강 처리: studyDetailId={}", studyDetail.getId());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -69,4 +69,8 @@ public static StudyDetail createStudyDetail(Study study, Long week, String atten
.assignment(Assignment.createEmptyAssignment())
.build();
}

public void cancelAssignment() {
assignment = Assignment.cancelAssignment();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package com.gdschongik.gdsc.domain.study.domain;

import com.gdschongik.gdsc.domain.member.domain.Member;
import com.gdschongik.gdsc.global.annotation.DomainService;
import com.gdschongik.gdsc.global.exception.CustomException;
import com.gdschongik.gdsc.global.exception.ErrorCode;

@DomainService
public class StudyDetailValidator {

public void validateCancelStudyAssignment(Member currentMember, StudyDetail studyDetail) {
validateMemberIsMentor(currentMember, studyDetail);
}

// 멘토가 아니라면 과제를 휴강처리 할 수 없다.
private void validateMemberIsMentor(Member member, StudyDetail studyDetail) {
if (!member.equals(studyDetail.getStudy().getMentor())) {
throw new CustomException(ErrorCode.STUDY_DETAIL_NOT_MODIFIABLE_INVALID_ROLE);
}
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
package com.gdschongik.gdsc.domain.study.domain.vo;

import static com.gdschongik.gdsc.domain.study.domain.StudyStatus.*;

import com.gdschongik.gdsc.domain.study.domain.Difficulty;
import com.gdschongik.gdsc.domain.study.domain.StudyStatus;
import jakarta.persistence.Column;
Expand Down Expand Up @@ -46,6 +48,10 @@ private Assignment(
}

public static Assignment createEmptyAssignment() {
return Assignment.builder().status(StudyStatus.NONE).build();
return Assignment.builder().status(NONE).build();
}

public static Assignment cancelAssignment() {
return Assignment.builder().status(CANCELLED).build();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,7 @@ public enum ErrorCode {

// StudyDetail
STUDY_DETAIL_NOT_FOUND(HttpStatus.NOT_FOUND, "존재하지 않는 스터디 상세 정보입니다."),
STUDY_DETAIL_NOT_MODIFIABLE_INVALID_ROLE(HttpStatus.FORBIDDEN, "수정할 수 있는 권한이 없습니다."),

// StudyHistory
STUDY_HISTORY_NOT_FOUND(HttpStatus.NOT_FOUND, "존재하지 않는 스터디 수강 기록입니다."),
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
package com.gdschongik.gdsc.domain.study.application;

import static org.assertj.core.api.Assertions.*;

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.dao.StudyDetailRepository;
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.helper.IntegrationTest;
import java.time.LocalDateTime;
import org.junit.jupiter.api.Nested;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;

public class StudyMentorServiceTest extends IntegrationTest {

@Autowired
private StudyMentorService studyMentorService;

@Autowired
private StudyDetailRepository studyDetailRepository;

@Nested
class 스터디_과제_휴강_처리시 {

@Test
void 성공한다() {
// given
LocalDateTime now = LocalDateTime.now();
Member mentor = createAssociateMember();
Study study = createStudy(
mentor,
Period.createPeriod(now.plusDays(5), now.plusDays(10)),
Period.createPeriod(now.minusDays(5), now));
StudyDetail studyDetail = createStudyDetail(study, now, now.plusDays(7));
logoutAndReloginAs(studyDetail.getStudy().getMentor().getId(), MemberRole.ASSOCIATE);

// when
studyMentorService.cancelStudyAssignment(studyDetail.getId());

// then
StudyDetail cancelledStudyDetail =
studyDetailRepository.findById(studyDetail.getId()).get();
assertThat(cancelledStudyDetail.getAssignment().getStatus()).isEqualTo(StudyStatus.CANCELLED);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
package com.gdschongik.gdsc.domain.study.domain;

import static org.assertj.core.api.Assertions.*;

import com.gdschongik.gdsc.domain.member.domain.Member;
import com.gdschongik.gdsc.domain.recruitment.domain.vo.Period;
import com.gdschongik.gdsc.helper.FixtureHelper;
import java.time.LocalDateTime;
import org.junit.jupiter.api.Nested;
import org.junit.jupiter.api.Test;

public class StudyDetailTest {

@Nested
class 과제_휴강_처리시 {

FixtureHelper fixtureHelper = new FixtureHelper();

@Test
void 과제_상태가_휴강이_된다() {
// given
Member mentor = fixtureHelper.createAssociateMember(1L);
LocalDateTime now = LocalDateTime.now();
Study study = fixtureHelper.createStudy(
mentor,
Period.createPeriod(now.plusDays(5), now.plusDays(10)),
Period.createPeriod(now.minusDays(5), now));
StudyDetail studyDetail = fixtureHelper.createStudyDetail(study, now, now.plusDays(7));

// when
studyDetail.cancelAssignment();

// then
assertThat(studyDetail.getAssignment().getStatus()).isEqualTo(StudyStatus.CANCELLED);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
package com.gdschongik.gdsc.domain.study.domain;

import static com.gdschongik.gdsc.global.exception.ErrorCode.*;
import static org.assertj.core.api.Assertions.*;

import com.gdschongik.gdsc.domain.member.domain.Member;
import com.gdschongik.gdsc.domain.recruitment.domain.vo.Period;
import com.gdschongik.gdsc.global.exception.CustomException;
import com.gdschongik.gdsc.helper.FixtureHelper;
import java.time.LocalDateTime;
import org.junit.jupiter.api.Nested;
import org.junit.jupiter.api.Test;

public class StudyDetailValidatorTest {

FixtureHelper fixtureHelper = new FixtureHelper();
StudyDetailValidator studyDetailValidator = new StudyDetailValidator();

@Nested
class 과제_휴강_처리시 {

@Test
void 멘토가_아니라면_실패한다() {
// given
LocalDateTime now = LocalDateTime.now();
Member mentor = fixtureHelper.createAssociateMember(1L);
Study study = fixtureHelper.createStudy(
mentor,
Period.createPeriod(now.plusDays(5), now.plusDays(10)),
Period.createPeriod(now.minusDays(5), now));
StudyDetail studyDetail = fixtureHelper.createStudyDetail(study, now, now.plusDays(7));
Member anotherMember = fixtureHelper.createAssociateMember(2L);

// when & then
assertThatThrownBy(() -> studyDetailValidator.validateCancelStudyAssignment(anotherMember, studyDetail))
.isInstanceOf(CustomException.class)
.hasMessage(STUDY_DETAIL_NOT_MODIFIABLE_INVALID_ROLE.getMessage());
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,4 +13,7 @@ private StudyConstant() {}
public static final DayOfWeek DAY_OF_WEEK = DayOfWeek.FRIDAY;
public static final LocalTime STUDY_START_TIME = LocalTime.of(19, 0, 0);
public static final LocalTime STUDY_END_TIME = LocalTime.of(20, 0, 0);

// StudyDetail
public static final String ATTENDANCE_NUMBER = "1234";
}
5 changes: 5 additions & 0 deletions src/test/java/com/gdschongik/gdsc/helper/FixtureHelper.java
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
import com.gdschongik.gdsc.domain.recruitment.domain.RoundType;
import com.gdschongik.gdsc.domain.recruitment.domain.vo.Period;
import com.gdschongik.gdsc.domain.study.domain.Study;
import com.gdschongik.gdsc.domain.study.domain.StudyDetail;
import java.time.LocalDateTime;
import org.springframework.test.util.ReflectionTestUtils;

Expand Down Expand Up @@ -79,4 +80,8 @@ public Study createStudy(Member mentor, Period period, Period applicationPeriod)
STUDY_START_TIME,
STUDY_END_TIME);
}

public StudyDetail createStudyDetail(Study study, LocalDateTime startDate, LocalDateTime endDate) {
return StudyDetail.createStudyDetail(study, 1L, ATTENDANCE_NUMBER, Period.createPeriod(startDate, endDate));
}
}
33 changes: 33 additions & 0 deletions src/test/java/com/gdschongik/gdsc/helper/IntegrationTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import static com.gdschongik.gdsc.global.common.constant.MemberConstant.*;
import static com.gdschongik.gdsc.global.common.constant.RecruitmentConstant.*;
import static com.gdschongik.gdsc.global.common.constant.SemesterConstant.*;
import static com.gdschongik.gdsc.global.common.constant.StudyConstant.*;
import static org.mockito.Mockito.*;

import com.gdschongik.gdsc.domain.common.model.SemesterType;
Expand All @@ -25,6 +26,10 @@
import com.gdschongik.gdsc.domain.recruitment.domain.RecruitmentRound;
import com.gdschongik.gdsc.domain.recruitment.domain.RoundType;
import com.gdschongik.gdsc.domain.recruitment.domain.vo.Period;
import com.gdschongik.gdsc.domain.study.dao.StudyDetailRepository;
import com.gdschongik.gdsc.domain.study.dao.StudyRepository;
import com.gdschongik.gdsc.domain.study.domain.Study;
import com.gdschongik.gdsc.domain.study.domain.StudyDetail;
import com.gdschongik.gdsc.global.security.PrincipalDetails;
import com.gdschongik.gdsc.infra.feign.payment.client.PaymentClient;
import java.time.LocalDateTime;
Expand Down Expand Up @@ -62,6 +67,12 @@ public abstract class IntegrationTest {
@Autowired
protected RecruitmentRoundRepository recruitmentRoundRepository;

@Autowired
protected StudyRepository studyRepository;

@Autowired
protected StudyDetailRepository studyDetailRepository;

@MockBean
protected OnboardingRecruitmentService onboardingRecruitmentService;

Expand Down Expand Up @@ -160,4 +171,26 @@ protected IssuedCoupon createAndIssue(Money money, Member member) {
IssuedCoupon issuedCoupon = IssuedCoupon.issue(coupon, member);
return issuedCouponRepository.save(issuedCoupon);
}

protected Study createStudy(Member mentor, Period period, Period applicationPeriod) {
Study study = Study.createStudy(
ACADEMIC_YEAR,
SEMESTER_TYPE,
mentor,
period,
applicationPeriod,
TOTAL_WEEK,
ONLINE_STUDY,
DAY_OF_WEEK,
STUDY_START_TIME,
STUDY_END_TIME);

return studyRepository.save(study);
}

protected StudyDetail createStudyDetail(Study study, LocalDateTime startDate, LocalDateTime endDate) {
StudyDetail studyDetail =
StudyDetail.createStudyDetail(study, 1L, ATTENDANCE_NUMBER, Period.createPeriod(startDate, endDate));
return studyDetailRepository.save(studyDetail);
}
}

0 comments on commit 6acc16c

Please sign in to comment.