-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* feat: 우수 스터디원 지정 및 철회 API 구현 * feat: 우수 스터디원 지정 및 철회 로직 추가 * test: 우수 스터디원 지정 및 철회 테스트 추가 * feat: 수강신청 여부 검증 로직 추가 * test: 수강신청 여부 검증 로직 테스트 추가 * refactor: 레포지토리 메서드명 변경 * refactor: getById 메서드를 findById로 대체 * refactor: NPE 방지 코드 추가 * refactor: 타입 캐스팅으로 변경
- Loading branch information
1 parent
a2e3a40
commit f12092a
Showing
13 changed files
with
300 additions
and
1 deletion.
There are no files selected for viewing
40 changes: 40 additions & 0 deletions
40
src/main/java/com/gdschongik/gdsc/domain/study/api/MentorStudyAchievementController.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,40 @@ | ||
package com.gdschongik.gdsc.domain.study.api; | ||
|
||
import com.gdschongik.gdsc.domain.study.application.MentorStudyAchievementService; | ||
import com.gdschongik.gdsc.domain.study.dto.request.OutstandingStudentRequest; | ||
import io.swagger.v3.oas.annotations.Operation; | ||
import io.swagger.v3.oas.annotations.tags.Tag; | ||
import jakarta.validation.Valid; | ||
import lombok.RequiredArgsConstructor; | ||
import org.springframework.http.ResponseEntity; | ||
import org.springframework.web.bind.annotation.DeleteMapping; | ||
import org.springframework.web.bind.annotation.PostMapping; | ||
import org.springframework.web.bind.annotation.RequestBody; | ||
import org.springframework.web.bind.annotation.RequestMapping; | ||
import org.springframework.web.bind.annotation.RequestParam; | ||
import org.springframework.web.bind.annotation.RestController; | ||
|
||
@Tag(name = "Mentor StudyAchievement", description = "멘토 스터디 우수 스터디원 관리 API입니다.") | ||
@RestController | ||
@RequestMapping("/mentor/study-achievements") | ||
@RequiredArgsConstructor | ||
public class MentorStudyAchievementController { | ||
|
||
private final MentorStudyAchievementService mentorStudyAchievementService; | ||
|
||
@Operation(summary = "우수 스터디원 지정", description = "우수 스터디원으로 지정합니다.") | ||
@PostMapping | ||
public ResponseEntity<Void> designateOutstandingStudent( | ||
@RequestParam(name = "studyId") Long studyId, @Valid @RequestBody OutstandingStudentRequest request) { | ||
mentorStudyAchievementService.designateOutstandingStudent(studyId, request); | ||
return ResponseEntity.ok().build(); | ||
} | ||
|
||
@Operation(summary = "우수 스터디원 철회", description = "우수 스터디원 지정을 철회합니다.") | ||
@DeleteMapping | ||
public ResponseEntity<Void> withdrawOutstandingStudent( | ||
@RequestParam(name = "studyId") Long studyId, @Valid @RequestBody OutstandingStudentRequest request) { | ||
mentorStudyAchievementService.withdrawOutstandingStudent(studyId, request); | ||
return ResponseEntity.ok().build(); | ||
} | ||
} |
74 changes: 74 additions & 0 deletions
74
...main/java/com/gdschongik/gdsc/domain/study/application/MentorStudyAchievementService.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,74 @@ | ||
package com.gdschongik.gdsc.domain.study.application; | ||
|
||
import static com.gdschongik.gdsc.global.exception.ErrorCode.*; | ||
|
||
import com.gdschongik.gdsc.domain.member.dao.MemberRepository; | ||
import com.gdschongik.gdsc.domain.member.domain.Member; | ||
import com.gdschongik.gdsc.domain.study.dao.StudyAchievementRepository; | ||
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.StudyAchievement; | ||
import com.gdschongik.gdsc.domain.study.domain.StudyHistoryValidator; | ||
import com.gdschongik.gdsc.domain.study.domain.StudyValidator; | ||
import com.gdschongik.gdsc.domain.study.dto.request.OutstandingStudentRequest; | ||
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; | ||
import org.springframework.stereotype.Service; | ||
import org.springframework.transaction.annotation.Transactional; | ||
|
||
@Slf4j | ||
@Service | ||
@RequiredArgsConstructor | ||
public class MentorStudyAchievementService { | ||
|
||
private final MemberUtil memberUtil; | ||
private final StudyValidator studyValidator; | ||
private final StudyHistoryValidator studyHistoryValidator; | ||
private final StudyRepository studyRepository; | ||
private final StudyHistoryRepository studyHistoryRepository; | ||
private final StudyAchievementRepository studyAchievementRepository; | ||
private final MemberRepository memberRepository; | ||
|
||
@Transactional | ||
public void designateOutstandingStudent(Long studyId, OutstandingStudentRequest request) { | ||
Member currentMember = memberUtil.getCurrentMember(); | ||
Study study = studyRepository.findById(studyId).orElseThrow(() -> new CustomException(STUDY_NOT_FOUND)); | ||
Long countByStudyIdAndStudentIds = | ||
studyHistoryRepository.countByStudyIdAndStudentIds(studyId, request.studentIds()); | ||
|
||
studyValidator.validateStudyMentor(currentMember, study); | ||
studyHistoryValidator.validateAppliedToStudy( | ||
countByStudyIdAndStudentIds, request.studentIds().size()); | ||
|
||
List<Member> outstandingStudents = memberRepository.findAllById(request.studentIds()); | ||
List<StudyAchievement> studyAchievements = outstandingStudents.stream() | ||
.map(member -> StudyAchievement.create(member, study, request.achievementType())) | ||
.toList(); | ||
studyAchievementRepository.saveAll(studyAchievements); | ||
|
||
log.info( | ||
"[MentorStudyAchievementService] 우수 스터디원 지정: studyId={}, studentIds={}", studyId, request.studentIds()); | ||
} | ||
|
||
@Transactional | ||
public void withdrawOutstandingStudent(Long studyId, OutstandingStudentRequest request) { | ||
Member currentMember = memberUtil.getCurrentMember(); | ||
Study study = studyRepository.findById(studyId).orElseThrow(() -> new CustomException(STUDY_NOT_FOUND)); | ||
long countByStudyIdAndStudentIds = | ||
studyHistoryRepository.countByStudyIdAndStudentIds(studyId, request.studentIds()); | ||
|
||
studyValidator.validateStudyMentor(currentMember, study); | ||
studyHistoryValidator.validateAppliedToStudy( | ||
countByStudyIdAndStudentIds, request.studentIds().size()); | ||
|
||
studyAchievementRepository.deleteByStudyAndAchievementTypeAndMemberIds( | ||
studyId, request.achievementType(), request.studentIds()); | ||
|
||
log.info( | ||
"[MentorStudyAchievementService] 우수 스터디원 철회: studyId={}, studentIds={}", studyId, request.studentIds()); | ||
} | ||
} |
4 changes: 4 additions & 0 deletions
4
src/main/java/com/gdschongik/gdsc/domain/study/dao/StudyAchievementCustomRepository.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,8 +1,12 @@ | ||
package com.gdschongik.gdsc.domain.study.dao; | ||
|
||
import com.gdschongik.gdsc.domain.study.domain.AchievementType; | ||
import com.gdschongik.gdsc.domain.study.domain.StudyAchievement; | ||
import java.util.List; | ||
|
||
public interface StudyAchievementCustomRepository { | ||
List<StudyAchievement> findByStudyIdAndMemberIds(Long studyId, List<Long> memberIds); | ||
|
||
void deleteByStudyAndAchievementTypeAndMemberIds( | ||
Long studyId, AchievementType achievementType, List<Long> memberIds); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
8 changes: 8 additions & 0 deletions
8
src/main/java/com/gdschongik/gdsc/domain/study/dao/StudyHistoryCustomRepository.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
package com.gdschongik.gdsc.domain.study.dao; | ||
|
||
import java.util.List; | ||
|
||
public interface StudyHistoryCustomRepository { | ||
|
||
long countByStudyIdAndStudentIds(Long studyId, List<Long> studentIds); | ||
} |
27 changes: 27 additions & 0 deletions
27
src/main/java/com/gdschongik/gdsc/domain/study/dao/StudyHistoryCustomRepositoryImpl.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
package com.gdschongik.gdsc.domain.study.dao; | ||
|
||
import static com.gdschongik.gdsc.domain.study.domain.QStudyHistory.*; | ||
|
||
import com.querydsl.core.types.dsl.BooleanExpression; | ||
import com.querydsl.jpa.impl.JPAQueryFactory; | ||
import java.util.List; | ||
import lombok.RequiredArgsConstructor; | ||
|
||
@RequiredArgsConstructor | ||
public class StudyHistoryCustomRepositoryImpl implements StudyHistoryCustomRepository { | ||
|
||
private final JPAQueryFactory queryFactory; | ||
|
||
@Override | ||
public long countByStudyIdAndStudentIds(Long studyId, List<Long> studentIds) { | ||
return (long) queryFactory | ||
.select(studyHistory.count()) | ||
.from(studyHistory) | ||
.where(eqStudyId(studyId), studyHistory.student.id.in(studentIds)) | ||
.fetchOne(); | ||
} | ||
|
||
private BooleanExpression eqStudyId(Long studyId) { | ||
return studyHistory.study.id.eq(studyId); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
6 changes: 6 additions & 0 deletions
6
src/main/java/com/gdschongik/gdsc/domain/study/dto/request/OutstandingStudentRequest.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
package com.gdschongik.gdsc.domain.study.dto.request; | ||
|
||
import com.gdschongik.gdsc.domain.study.domain.AchievementType; | ||
import java.util.List; | ||
|
||
public record OutstandingStudentRequest(List<Long> studentIds, AchievementType achievementType) {} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
82 changes: 82 additions & 0 deletions
82
.../java/com/gdschongik/gdsc/domain/study/application/MentorStudyAchievementServiceTest.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,82 @@ | ||
package com.gdschongik.gdsc.domain.study.application; | ||
|
||
import static com.gdschongik.gdsc.domain.study.domain.AchievementType.*; | ||
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.domain.study.domain.Study; | ||
import com.gdschongik.gdsc.domain.study.domain.StudyAchievement; | ||
import com.gdschongik.gdsc.domain.study.dto.request.OutstandingStudentRequest; | ||
import com.gdschongik.gdsc.helper.IntegrationTest; | ||
import java.time.LocalDateTime; | ||
import java.util.List; | ||
import org.junit.jupiter.api.Nested; | ||
import org.junit.jupiter.api.Test; | ||
import org.springframework.beans.factory.annotation.Autowired; | ||
|
||
public class MentorStudyAchievementServiceTest extends IntegrationTest { | ||
|
||
@Autowired | ||
private MentorStudyAchievementService mentorStudyAchievementService; | ||
|
||
@Nested | ||
class 우수_스터디원_지정시 { | ||
|
||
@Test | ||
void 성공한다() { | ||
// given | ||
LocalDateTime now = LocalDateTime.now(); | ||
Member mentor = createMentor(); | ||
Study study = createStudy( | ||
mentor, | ||
Period.createPeriod(now.plusDays(5), now.plusDays(10)), | ||
Period.createPeriod(now.minusDays(5), now)); | ||
|
||
Member student = createRegularMember(); | ||
createStudyHistory(student, study); | ||
|
||
logoutAndReloginAs(mentor.getId(), mentor.getRole()); | ||
OutstandingStudentRequest request = | ||
new OutstandingStudentRequest(List.of(student.getId()), FIRST_ROUND_OUTSTANDING_STUDENT); | ||
|
||
// when | ||
mentorStudyAchievementService.designateOutstandingStudent(study.getId(), request); | ||
|
||
// then | ||
List<StudyAchievement> studyAchievements = | ||
studyAchievementRepository.findByStudyIdAndMemberIds(study.getId(), request.studentIds()); | ||
assertThat(studyAchievements).hasSize(request.studentIds().size()); | ||
} | ||
} | ||
|
||
@Nested | ||
class 우수_스터디원_철회시 { | ||
|
||
@Test | ||
void 성공한다() { | ||
// given | ||
Member student = createRegularMember(); | ||
LocalDateTime now = LocalDateTime.now(); | ||
Member mentor = createMentor(); | ||
Study study = createStudy( | ||
mentor, | ||
Period.createPeriod(now.plusDays(5), now.plusDays(10)), | ||
Period.createPeriod(now.minusDays(5), now)); | ||
createStudyHistory(student, study); | ||
createStudyAchievement(student, study, FIRST_ROUND_OUTSTANDING_STUDENT); | ||
|
||
logoutAndReloginAs(mentor.getId(), mentor.getRole()); | ||
OutstandingStudentRequest request = | ||
new OutstandingStudentRequest(List.of(student.getId()), FIRST_ROUND_OUTSTANDING_STUDENT); | ||
|
||
// when | ||
mentorStudyAchievementService.withdrawOutstandingStudent(study.getId(), request); | ||
|
||
// then | ||
List<StudyAchievement> studyAchievements = | ||
studyAchievementRepository.findByStudyIdAndMemberIds(study.getId(), request.studentIds()); | ||
assertThat(studyAchievements).isEmpty(); | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters