diff --git a/src/main/java/com/example/solidconnection/admin/controller/AdminMentorApplicationController.java b/src/main/java/com/example/solidconnection/admin/controller/AdminMentorApplicationController.java index b8fe69742..5895a81dc 100644 --- a/src/main/java/com/example/solidconnection/admin/controller/AdminMentorApplicationController.java +++ b/src/main/java/com/example/solidconnection/admin/controller/AdminMentorApplicationController.java @@ -2,12 +2,14 @@ import com.example.solidconnection.admin.dto.MentorApplicationAssignUniversityRequest; import com.example.solidconnection.admin.dto.MentorApplicationCountResponse; +import com.example.solidconnection.admin.dto.MentorApplicationHistoryResponse; import com.example.solidconnection.admin.dto.MentorApplicationRejectRequest; import com.example.solidconnection.admin.dto.MentorApplicationSearchCondition; import com.example.solidconnection.admin.dto.MentorApplicationSearchResponse; import com.example.solidconnection.admin.service.AdminMentorApplicationService; import com.example.solidconnection.common.response.PageResponse; import jakarta.validation.Valid; +import java.util.List; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.data.domain.Page; @@ -73,4 +75,12 @@ public ResponseEntity assignUniversity( adminMentorApplicationService.assignUniversity(mentorApplicationId, universityId); return ResponseEntity.ok().build(); } + + @GetMapping("/{site-user-id}/mentor-application-history") + public ResponseEntity> getMentorApplicationHistory( + @PathVariable("site-user-id") Long siteUserId + ){ + List response = adminMentorApplicationService.findMentorApplicationHistory(siteUserId); + return ResponseEntity.ok(response); + } } diff --git a/src/main/java/com/example/solidconnection/admin/dto/MentorApplicationHistoryResponse.java b/src/main/java/com/example/solidconnection/admin/dto/MentorApplicationHistoryResponse.java new file mode 100644 index 000000000..46f7493dc --- /dev/null +++ b/src/main/java/com/example/solidconnection/admin/dto/MentorApplicationHistoryResponse.java @@ -0,0 +1,14 @@ +package com.example.solidconnection.admin.dto; + +import com.example.solidconnection.mentor.domain.MentorApplicationStatus; +import java.time.ZonedDateTime; + +public record MentorApplicationHistoryResponse( + long id, + MentorApplicationStatus mentorApplicationStatus, + String rejectedReason, + ZonedDateTime createdAt, + int applicationOrder +) { + +} diff --git a/src/main/java/com/example/solidconnection/admin/service/AdminMentorApplicationService.java b/src/main/java/com/example/solidconnection/admin/service/AdminMentorApplicationService.java index d22d9af7f..dfa71b56f 100644 --- a/src/main/java/com/example/solidconnection/admin/service/AdminMentorApplicationService.java +++ b/src/main/java/com/example/solidconnection/admin/service/AdminMentorApplicationService.java @@ -1,8 +1,10 @@ package com.example.solidconnection.admin.service; import static com.example.solidconnection.common.exception.ErrorCode.MENTOR_APPLICATION_NOT_FOUND; +import static com.example.solidconnection.common.exception.ErrorCode.USER_NOT_FOUND; import com.example.solidconnection.admin.dto.MentorApplicationCountResponse; +import com.example.solidconnection.admin.dto.MentorApplicationHistoryResponse; import com.example.solidconnection.admin.dto.MentorApplicationRejectRequest; import com.example.solidconnection.admin.dto.MentorApplicationSearchCondition; import com.example.solidconnection.admin.dto.MentorApplicationSearchResponse; @@ -10,8 +12,12 @@ import com.example.solidconnection.mentor.domain.MentorApplication; import com.example.solidconnection.mentor.domain.MentorApplicationStatus; import com.example.solidconnection.mentor.repository.MentorApplicationRepository; +import com.example.solidconnection.siteuser.domain.SiteUser; +import com.example.solidconnection.siteuser.repository.SiteUserRepository; import com.example.solidconnection.university.domain.University; import com.example.solidconnection.university.repository.UniversityRepository; +import java.util.List; +import java.util.stream.IntStream; import lombok.RequiredArgsConstructor; import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; @@ -24,6 +30,7 @@ public class AdminMentorApplicationService { private final MentorApplicationRepository mentorApplicationRepository; private final UniversityRepository universityRepository; + private final SiteUserRepository siteUserRepository; @Transactional(readOnly = true) public Page searchMentorApplications( @@ -43,7 +50,7 @@ public void approveMentorApplication(Long mentorApplicationId) { @Transactional public void rejectMentorApplication( - long mentorApplicationId, + Long mentorApplicationId, MentorApplicationRejectRequest request ) { MentorApplication mentorApplication = mentorApplicationRepository.findById(mentorApplicationId) @@ -79,4 +86,26 @@ public void assignUniversity( mentorApplication.assignUniversity(university.getId()); } + + @Transactional(readOnly = true) + public List findMentorApplicationHistory(Long siteUserId) { + SiteUser siteUser = siteUserRepository.findById(siteUserId) + .orElseThrow(() -> new CustomException(USER_NOT_FOUND)); + + long totalCount = mentorApplicationRepository.countBySiteUserId(siteUserId); + + List mentorApplications = mentorApplicationRepository.findTop5BySiteUserIdOrderByCreatedAtDesc(siteUser.getId()); + + return IntStream.range(0, mentorApplications.size()) + .mapToObj(index -> { + MentorApplication app = mentorApplications.get(index); + return new MentorApplicationHistoryResponse( + app.getId(), + app.getMentorApplicationStatus(), + app.getRejectedReason(), + app.getCreatedAt(), + (int) totalCount - index + ); + }).toList(); + } } diff --git a/src/main/java/com/example/solidconnection/mentor/repository/MentorApplicationRepository.java b/src/main/java/com/example/solidconnection/mentor/repository/MentorApplicationRepository.java index 144b5461c..61339819a 100644 --- a/src/main/java/com/example/solidconnection/mentor/repository/MentorApplicationRepository.java +++ b/src/main/java/com/example/solidconnection/mentor/repository/MentorApplicationRepository.java @@ -14,4 +14,8 @@ public interface MentorApplicationRepository extends JpaRepository findBySiteUserIdAndMentorApplicationStatus(long siteUserId, MentorApplicationStatus mentorApplicationStatus); long countByMentorApplicationStatus(MentorApplicationStatus mentorApplicationStatus); + + List findTop5BySiteUserIdOrderByCreatedAtDesc(long siteUserId); + + long countBySiteUserId(long siteUserId); } diff --git a/src/main/resources/secret b/src/main/resources/secret index 29524e2d6..8300cdeca 160000 --- a/src/main/resources/secret +++ b/src/main/resources/secret @@ -1 +1 @@ -Subproject commit 29524e2d6dad2042400de0370a11893029aacff2 +Subproject commit 8300cdecaebfc28fd657064a00a44815a7bb2eee diff --git a/src/test/java/com/example/solidconnection/admin/service/AdminMentorApplicationServiceTest.java b/src/test/java/com/example/solidconnection/admin/service/AdminMentorApplicationServiceTest.java index ae40cb5d7..4a3c1ad9e 100644 --- a/src/test/java/com/example/solidconnection/admin/service/AdminMentorApplicationServiceTest.java +++ b/src/test/java/com/example/solidconnection/admin/service/AdminMentorApplicationServiceTest.java @@ -5,11 +5,13 @@ import static com.example.solidconnection.common.exception.ErrorCode.MENTOR_APPLICATION_NOT_OTHER_STATUS; import static com.example.solidconnection.common.exception.ErrorCode.MENTOR_APPLICATION_UNIVERSITY_NOT_SELECTED; import static com.example.solidconnection.common.exception.ErrorCode.UNIVERSITY_NOT_FOUND; +import static com.example.solidconnection.common.exception.ErrorCode.USER_NOT_FOUND; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.AssertionsForClassTypes.assertThatCode; import static org.junit.jupiter.api.Assertions.assertAll; import com.example.solidconnection.admin.dto.MentorApplicationCountResponse; +import com.example.solidconnection.admin.dto.MentorApplicationHistoryResponse; import com.example.solidconnection.admin.dto.MentorApplicationRejectRequest; import com.example.solidconnection.admin.dto.MentorApplicationSearchCondition; import com.example.solidconnection.admin.dto.MentorApplicationSearchResponse; @@ -509,4 +511,106 @@ class 멘토_지원서에_대학_매핑 { .hasMessage(UNIVERSITY_NOT_FOUND.getMessage()); } } + + @Nested + class 멘토_지원서_이력_조회 { + + @Test + void 사용자의_멘토_지원서_이력을_최신_생성_내림차순으로_조회한다() { + // given + SiteUser user = siteUserFixture.사용자(); + University university = universityFixture.메이지_대학(); + MentorApplication app1 = mentorApplicationFixture.거절된_멘토신청(user.getId(), UniversitySelectType.CATALOG, university.getId()); + MentorApplication app2 = mentorApplicationFixture.거절된_멘토신청(user.getId(), UniversitySelectType.CATALOG, university.getId()); + MentorApplication app3 = mentorApplicationFixture.승인된_멘토신청(user.getId(), UniversitySelectType.CATALOG, university.getId()); + + // when + List response = adminMentorApplicationService.findMentorApplicationHistory(user.getId()); + + // then + assertAll( + () -> assertThat(response).hasSize(3), + () -> assertThat(response) + .extracting(MentorApplicationHistoryResponse::id) + .containsExactly(app3.getId(), app2.getId(), app1.getId()), + () -> assertThat(response) + .extracting(MentorApplicationHistoryResponse::applicationOrder) + .containsExactly(3,2,1) + ); + } + + @Test + void 지원서가_5개를_초과하면_최신_5개만_최신_생성_내림차순으로_조회한다() { + // given + SiteUser user = siteUserFixture.사용자(); + University university = universityFixture.메이지_대학(); + MentorApplication app1 = mentorApplicationFixture.거절된_멘토신청(user.getId(), UniversitySelectType.CATALOG, university.getId()); + MentorApplication app2 = mentorApplicationFixture.거절된_멘토신청(user.getId(), UniversitySelectType.CATALOG, university.getId()); + MentorApplication app3 = mentorApplicationFixture.거절된_멘토신청(user.getId(), UniversitySelectType.CATALOG, university.getId()); + MentorApplication app4 = mentorApplicationFixture.거절된_멘토신청(user.getId(), UniversitySelectType.CATALOG, university.getId()); + MentorApplication app5 = mentorApplicationFixture.거절된_멘토신청(user.getId(), UniversitySelectType.CATALOG, university.getId()); + MentorApplication app6 = mentorApplicationFixture.거절된_멘토신청(user.getId(), UniversitySelectType.CATALOG, university.getId()); + MentorApplication app7 = mentorApplicationFixture.승인된_멘토신청(user.getId(), UniversitySelectType.CATALOG, university.getId()); + + // when + List response = adminMentorApplicationService.findMentorApplicationHistory(user.getId()); + + // then + assertAll( + () -> assertThat(response).hasSize(5), + () -> assertThat(response) + .extracting(MentorApplicationHistoryResponse::id) + .containsExactly(app7.getId(), app6.getId(), app5.getId(), app4.getId(), app3.getId()), + () -> assertThat(response) + .extracting(MentorApplicationHistoryResponse::applicationOrder) + .containsExactly(7,6,5,4,3) + ); + } + + @Test + void 지원서_이력이_없으면_빈_목록을_반환한다() { + // given + SiteUser user = siteUserFixture.사용자(); + + // when + List response = adminMentorApplicationService.findMentorApplicationHistory(user.getId()); + + // then + assertThat(response).isEmpty(); + } + + @Test + void 응답에_지원서_상태와_거절_사유가_포함된다() { + // given + SiteUser user = siteUserFixture.사용자(); + University university = universityFixture.메이지_대학(); + mentorApplicationFixture.거절된_멘토신청(user.getId(), UniversitySelectType.CATALOG, university.getId()); + mentorApplicationFixture.승인된_멘토신청(user.getId(), UniversitySelectType.CATALOG, university.getId()); + + // when + List response = adminMentorApplicationService.findMentorApplicationHistory(user.getId()); + + // then + assertAll( + () -> assertThat(response).hasSize(2), + () -> assertThat(response.get(0).mentorApplicationStatus()).isEqualTo(MentorApplicationStatus.APPROVED), + () -> assertThat(response.get(0).rejectedReason()).isNull(), + () -> assertThat(response.get(0).applicationOrder()).isEqualTo(2), + () -> assertThat(response.get(1).mentorApplicationStatus()).isEqualTo(MentorApplicationStatus.REJECTED), + () -> assertThat(response.get(1).rejectedReason()).isNotNull(), + () -> assertThat(response.get(1).applicationOrder()).isEqualTo(1) + ); + } + + @Test + void 존재하지_않는_사용자_이력을_조회하면_예외_응답을_반환한다() { + // given + long nonExistentUserId = 99999L; + + // when & then + assertThatCode(() -> adminMentorApplicationService.findMentorApplicationHistory(nonExistentUserId)) + .isInstanceOf(CustomException.class) + .hasMessage(USER_NOT_FOUND.getMessage()); + } + } } diff --git a/src/test/java/com/example/solidconnection/mentor/fixture/MentorApplicationFixture.java b/src/test/java/com/example/solidconnection/mentor/fixture/MentorApplicationFixture.java index 0baf62e2f..6a7153757 100644 --- a/src/test/java/com/example/solidconnection/mentor/fixture/MentorApplicationFixture.java +++ b/src/test/java/com/example/solidconnection/mentor/fixture/MentorApplicationFixture.java @@ -18,6 +18,7 @@ public class MentorApplicationFixture { private static final String DEFAULT_COUNTRY_CODE = "US"; private static final String DEFAULT_PROOF_URL = "/mentor-proof.pdf"; private static final ExchangeStatus DEFAULT_EXCHANGE_STATUS = ExchangeStatus.AFTER_EXCHANGE; + private static final String REJECTED_REASON = "pdf 파일 안열림"; public MentorApplication 대기중_멘토신청( long siteUserId, @@ -64,6 +65,7 @@ public class MentorApplicationFixture { .universitySelectType(selectType) .mentorProofUrl(DEFAULT_PROOF_URL) .termId(termFixture.현재_학기("2025-1").getId()) + .rejectedReason(REJECTED_REASON) .exchangeStatus(DEFAULT_EXCHANGE_STATUS) .mentorApplicationStatus(MentorApplicationStatus.REJECTED) .create(); diff --git a/src/test/java/com/example/solidconnection/mentor/fixture/MentorApplicationFixtureBuilder.java b/src/test/java/com/example/solidconnection/mentor/fixture/MentorApplicationFixtureBuilder.java index fd2a74ff6..93e5e24bc 100644 --- a/src/test/java/com/example/solidconnection/mentor/fixture/MentorApplicationFixtureBuilder.java +++ b/src/test/java/com/example/solidconnection/mentor/fixture/MentorApplicationFixtureBuilder.java @@ -21,6 +21,7 @@ public class MentorApplicationFixtureBuilder { private UniversitySelectType universitySelectType = UniversitySelectType.OTHER; private String mentorProofUrl = "/mentor-proof.pdf"; private long termId; + private String rejectedReason = null; private ExchangeStatus exchangeStatus = ExchangeStatus.AFTER_EXCHANGE; private MentorApplicationStatus mentorApplicationStatus = MentorApplicationStatus.PENDING; @@ -58,6 +59,11 @@ public MentorApplicationFixtureBuilder termId(long termId) { return this; } + public MentorApplicationFixtureBuilder rejectedReason(String rejectedReason) { + this.rejectedReason = rejectedReason; + return this; + } + public MentorApplicationFixtureBuilder exchangeStatus(ExchangeStatus exchangeStatus) { this.exchangeStatus = exchangeStatus; return this; @@ -79,6 +85,9 @@ public MentorApplication create() { exchangeStatus ); ReflectionTestUtils.setField(mentorApplication, "mentorApplicationStatus", mentorApplicationStatus); + if(rejectedReason != null) { + ReflectionTestUtils.setField(mentorApplication, "rejectedReason", rejectedReason); + } return mentorApplicationRepository.save(mentorApplication); } }