Skip to content

Commit 0e9d476

Browse files
authored
feat: 지원서가 APPROVED 인 유저의 멘토 생성 기능 추가 (#562)
* feat: 지원서가 APPROVED 인 유저의 멘토 생성 기능 추가 * refactor: submitMentorApplication 메서드의 멘토 지원 유효 검증 부분을 메서드로 추출 * refactor: MentorMyPage 생성, 수정 부분의 channel 생성, 업데이트 부분 중복 제거 * test: Mentor 생성 관련 테스트 추가 * fix: 코드래빗 리뷰 적용 * refactor: 멘토 생성 시 channelRequest가 null 일 떄 예외 처리 * feat: MentorApplicationRequest 필드에 유효성 어노테이션 추가 * test: 채널 검색 시 siteUserId로 조회하는 문제 해결 * fix: 리뷰 수정사항 적용 * fix: 파일 끝에 개행 추가 * refactor: 멘토 생성 메서드에서 siteUser의 검증 제외 * refactor: dto 단에서 채널 리스트 null 검증 * feat: MentorApplication에 termId 추가 flyway 스크립트 추가 * fix: flyway 버전 충돌 해결
1 parent 4da854b commit 0e9d476

File tree

16 files changed

+293
-13
lines changed

16 files changed

+293
-13
lines changed

src/main/java/com/example/solidconnection/common/exception/ErrorCode.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@ public enum ErrorCode {
5050
BLOCK_USER_NOT_FOUND(HttpStatus.NOT_FOUND.value(), "차단 대상 사용자를 찾을 수 없습니다."),
5151
TERM_NOT_FOUND(HttpStatus.NOT_FOUND.value(), "존재하지 않는 학기입니다."),
5252
CURRENT_TERM_NOT_FOUND(HttpStatus.NOT_FOUND.value(), "현재 학기를 찾을 수 없습니다."),
53+
MENTOR_APPLICATION_NOT_FOUND(HttpStatus.NOT_FOUND.value(), "멘토 지원서가 존재하지 않습니다."),
5354

5455
// auth
5556
USER_ALREADY_SIGN_OUT(HttpStatus.UNAUTHORIZED.value(), "로그아웃 되었습니다."),
@@ -127,6 +128,7 @@ public enum ErrorCode {
127128
UNIVERSITY_ID_REQUIRED_FOR_CATALOG(HttpStatus.BAD_REQUEST.value(), "목록에서 학교를 선택한 경우 학교 정보가 필요합니다."),
128129
UNIVERSITY_ID_MUST_BE_NULL_FOR_OTHER(HttpStatus.BAD_REQUEST.value(), "기타 학교를 선택한 경우 학교 정보를 입력할 수 없습니다."),
129130
INVALID_UNIVERSITY_SELECT_TYPE(HttpStatus.BAD_REQUEST.value(), "지원하지 않는 학교 선택 방식입니다."),
131+
MENTOR_ALREADY_EXISTS(HttpStatus.BAD_REQUEST.value(), "이미 존재하는 멘토입니다."),
130132

131133
// socket
132134
UNAUTHORIZED_SUBSCRIBE(HttpStatus.FORBIDDEN.value(), "구독 권한이 없습니다."),

src/main/java/com/example/solidconnection/mentor/controller/MentorMyPageController.java

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package com.example.solidconnection.mentor.controller;
22

33
import com.example.solidconnection.common.resolver.AuthorizedUser;
4+
import com.example.solidconnection.mentor.dto.MentorMyPageCreateRequest;
45
import com.example.solidconnection.mentor.dto.MentorMyPageResponse;
56
import com.example.solidconnection.mentor.dto.MentorMyPageUpdateRequest;
67
import com.example.solidconnection.mentor.service.MentorMyPageService;
@@ -10,6 +11,7 @@
1011
import lombok.RequiredArgsConstructor;
1112
import org.springframework.http.ResponseEntity;
1213
import org.springframework.web.bind.annotation.GetMapping;
14+
import org.springframework.web.bind.annotation.PostMapping;
1315
import org.springframework.web.bind.annotation.PutMapping;
1416
import org.springframework.web.bind.annotation.RequestBody;
1517
import org.springframework.web.bind.annotation.RequestMapping;
@@ -40,4 +42,14 @@ public ResponseEntity<Void> updateMentorMyPage(
4042
mentorMyPageService.updateMentorMyPage(siteUserId, mentorMyPageUpdateRequest);
4143
return ResponseEntity.ok().build();
4244
}
45+
46+
@RequireRoleAccess(roles = Role.MENTOR)
47+
@PostMapping
48+
public ResponseEntity<Void> createMentorMyPage(
49+
@AuthorizedUser long siteUserId,
50+
@Valid @RequestBody MentorMyPageCreateRequest request
51+
) {
52+
mentorMyPageService.createMentorMyPage(siteUserId, request);
53+
return ResponseEntity.ok().build();
54+
}
4355
}

src/main/java/com/example/solidconnection/mentor/domain/Mentor.java

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,20 @@ public class Mentor extends BaseEntity {
5353
@OneToMany(mappedBy = "mentor", cascade = CascadeType.ALL, orphanRemoval = true)
5454
private List<Channel> channels = new ArrayList<>();
5555

56+
public Mentor(
57+
String introduction,
58+
String passTip,
59+
long siteUserId,
60+
Long universityId,
61+
long termId
62+
) {
63+
this.introduction = introduction;
64+
this.passTip = passTip;
65+
this.siteUserId = siteUserId;
66+
this.universityId = universityId;
67+
this.termId = termId;
68+
}
69+
5670
public void increaseMenteeCount() {
5771
this.menteeCount++;
5872
}
@@ -82,4 +96,11 @@ public void updateChannels(List<Channel> channels) {
8296
}
8397
}
8498
}
99+
100+
public void createChannels(List<Channel> channels) {
101+
for(Channel channel : channels) {
102+
channel.updateMentor(this);
103+
this.channels.add(channel);
104+
}
105+
}
85106
}

src/main/java/com/example/solidconnection/mentor/domain/MentorApplication.java

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,9 @@ public class MentorApplication extends BaseEntity {
5353
@Column(nullable = false, name = "mentor_proof_url", length = 500)
5454
private String mentorProofUrl;
5555

56+
@Column(nullable = false, name = "term_id")
57+
private long termId;
58+
5659
private String rejectedReason;
5760

5861
@Column(nullable = false)
@@ -61,7 +64,7 @@ public class MentorApplication extends BaseEntity {
6164

6265
@Column(nullable = false)
6366
@Enumerated(EnumType.STRING)
64-
private MentorApplicationStatus mentorApplicationStatus = MentorApplicationStatus.PENDING;
67+
private MentorApplicationStatus mentorApplicationStatus;
6568

6669
private static final Set<ExchangeStatus> ALLOWED =
6770
Collections.unmodifiableSet(EnumSet.of(ExchangeStatus.STUDYING_ABROAD, ExchangeStatus.AFTER_EXCHANGE));
@@ -72,6 +75,7 @@ public MentorApplication(
7275
Long universityId,
7376
UniversitySelectType universitySelectType,
7477
String mentorProofUrl,
78+
long termId,
7579
ExchangeStatus exchangeStatus
7680
) {
7781
validateExchangeStatus(exchangeStatus);
@@ -82,7 +86,9 @@ public MentorApplication(
8286
this.universityId = universityId;
8387
this.universitySelectType = universitySelectType;
8488
this.mentorProofUrl = mentorProofUrl;
89+
this.termId = termId;
8590
this.exchangeStatus = exchangeStatus;
91+
this.mentorApplicationStatus = MentorApplicationStatus.PENDING;
8692
}
8793

8894
private void validateUniversitySelection(UniversitySelectType universitySelectType, Long universityId) {

src/main/java/com/example/solidconnection/mentor/dto/MentorApplicationRequest.java

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,23 @@
33
import com.example.solidconnection.mentor.domain.UniversitySelectType;
44
import com.example.solidconnection.siteuser.domain.ExchangeStatus;
55
import com.fasterxml.jackson.annotation.JsonProperty;
6+
import jakarta.validation.constraints.NotBlank;
7+
import jakarta.validation.constraints.NotNull;
68

79
public record MentorApplicationRequest(
10+
@NotNull(message = "교환 상태를 입력해주세요.")
811
@JsonProperty("preparationStatus")
912
ExchangeStatus exchangeStatus,
13+
14+
@NotNull(message = "대학교 선택 유형을 입력해주세요.")
1015
UniversitySelectType universitySelectType,
16+
17+
@NotNull(message = "국가를 입력해주세요")
1118
String country,
12-
Long universityId
19+
20+
Long universityId,
21+
22+
@NotBlank(message = "학기를 입력해주세요.")
23+
String term
1324
) {
1425
}
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
package com.example.solidconnection.mentor.dto;
2+
3+
import jakarta.validation.Valid;
4+
import jakarta.validation.constraints.NotBlank;
5+
import jakarta.validation.constraints.NotNull;
6+
import java.util.List;
7+
8+
public record MentorMyPageCreateRequest(
9+
@NotBlank(message = "자기소개를 입력해주세요.")
10+
String introduction,
11+
12+
@NotBlank(message = "합격 레시피를 입력해주세요.")
13+
String passTip,
14+
15+
@NotNull
16+
@Valid
17+
List<ChannelRequest> channels
18+
) {
19+
20+
}

src/main/java/com/example/solidconnection/mentor/repository/MentorApplicationRepository.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,12 @@
33
import com.example.solidconnection.mentor.domain.MentorApplication;
44
import com.example.solidconnection.mentor.domain.MentorApplicationStatus;
55
import java.util.List;
6+
import java.util.Optional;
67
import org.springframework.data.jpa.repository.JpaRepository;
78

89
public interface MentorApplicationRepository extends JpaRepository<MentorApplication, Long> {
910

1011
boolean existsBySiteUserIdAndMentorApplicationStatusIn(long siteUserId, List<MentorApplicationStatus> mentorApplicationStatuses);
12+
13+
Optional<MentorApplication> findBySiteUserIdAndMentorApplicationStatus(long siteUserId, MentorApplicationStatus mentorApplicationStatus);
1114
}

src/main/java/com/example/solidconnection/mentor/service/MentorApplicationService.java

Lines changed: 17 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@
1010
import com.example.solidconnection.s3.service.S3Service;
1111
import com.example.solidconnection.siteuser.domain.SiteUser;
1212
import com.example.solidconnection.siteuser.repository.SiteUserRepository;
13+
import com.example.solidconnection.term.domain.Term;
14+
import com.example.solidconnection.term.repository.TermRepository;
1315
import java.util.List;
1416
import lombok.RequiredArgsConstructor;
1517
import lombok.extern.slf4j.Slf4j;
@@ -18,6 +20,7 @@
1820
import org.springframework.web.multipart.MultipartFile;
1921

2022
import static com.example.solidconnection.common.exception.ErrorCode.MENTOR_APPLICATION_ALREADY_EXISTED;
23+
import static com.example.solidconnection.common.exception.ErrorCode.TERM_NOT_FOUND;
2124
import static com.example.solidconnection.common.exception.ErrorCode.USER_NOT_FOUND;
2225

2326
@Service
@@ -28,31 +31,39 @@ public class MentorApplicationService {
2831
private final MentorApplicationRepository mentorApplicationRepository;
2932
private final SiteUserRepository siteUserRepository;
3033
private final S3Service s3Service;
34+
private final TermRepository termRepository;
3135

3236
@Transactional
3337
public void submitMentorApplication(
3438
long siteUserId,
3539
MentorApplicationRequest mentorApplicationRequest,
3640
MultipartFile file
3741
) {
38-
if (mentorApplicationRepository.existsBySiteUserIdAndMentorApplicationStatusIn(
39-
siteUserId,
40-
List.of(MentorApplicationStatus.PENDING, MentorApplicationStatus.APPROVED))
41-
) {
42-
throw new CustomException(MENTOR_APPLICATION_ALREADY_EXISTED);
43-
}
42+
ensureNoPendingOrApprovedMentorApplication(siteUserId);
4443

4544
SiteUser siteUser = siteUserRepository.findById(siteUserId)
4645
.orElseThrow(() -> new CustomException(USER_NOT_FOUND));
46+
Term term = termRepository.findByName(mentorApplicationRequest.term())
47+
.orElseThrow(() -> new CustomException(TERM_NOT_FOUND));
4748
UploadedFileUrlResponse uploadedFile = s3Service.uploadFile(file, ImgType.MENTOR_PROOF);
4849
MentorApplication mentorApplication = new MentorApplication(
4950
siteUser.getId(),
5051
mentorApplicationRequest.country(),
5152
mentorApplicationRequest.universityId(),
5253
mentorApplicationRequest.universitySelectType(),
5354
uploadedFile.fileUrl(),
55+
term.getId(),
5456
mentorApplicationRequest.exchangeStatus()
5557
);
5658
mentorApplicationRepository.save(mentorApplication);
5759
}
60+
61+
private void ensureNoPendingOrApprovedMentorApplication(long siteUserId) {
62+
if (mentorApplicationRepository.existsBySiteUserIdAndMentorApplicationStatusIn(
63+
siteUserId,
64+
List.of(MentorApplicationStatus.PENDING, MentorApplicationStatus.APPROVED))
65+
) {
66+
throw new CustomException(MENTOR_APPLICATION_ALREADY_EXISTED);
67+
}
68+
}
5869
}

src/main/java/com/example/solidconnection/mentor/service/MentorMyPageService.java

Lines changed: 44 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
package com.example.solidconnection.mentor.service;
22

33
import static com.example.solidconnection.common.exception.ErrorCode.CHANNEL_REGISTRATION_LIMIT_EXCEEDED;
4+
import static com.example.solidconnection.common.exception.ErrorCode.MENTOR_ALREADY_EXISTS;
5+
import static com.example.solidconnection.common.exception.ErrorCode.MENTOR_APPLICATION_NOT_FOUND;
46
import static com.example.solidconnection.common.exception.ErrorCode.MENTOR_NOT_FOUND;
57
import static com.example.solidconnection.common.exception.ErrorCode.TERM_NOT_FOUND;
68
import static com.example.solidconnection.common.exception.ErrorCode.UNIVERSITY_NOT_FOUND;
@@ -9,9 +11,13 @@
911
import com.example.solidconnection.common.exception.CustomException;
1012
import com.example.solidconnection.mentor.domain.Channel;
1113
import com.example.solidconnection.mentor.domain.Mentor;
14+
import com.example.solidconnection.mentor.domain.MentorApplication;
15+
import com.example.solidconnection.mentor.domain.MentorApplicationStatus;
1216
import com.example.solidconnection.mentor.dto.ChannelRequest;
17+
import com.example.solidconnection.mentor.dto.MentorMyPageCreateRequest;
1318
import com.example.solidconnection.mentor.dto.MentorMyPageResponse;
1419
import com.example.solidconnection.mentor.dto.MentorMyPageUpdateRequest;
20+
import com.example.solidconnection.mentor.repository.MentorApplicationRepository;
1521
import com.example.solidconnection.mentor.repository.MentorRepository;
1622
import com.example.solidconnection.siteuser.domain.SiteUser;
1723
import com.example.solidconnection.siteuser.repository.SiteUserRepository;
@@ -36,6 +42,7 @@ public class MentorMyPageService {
3642
private final SiteUserRepository siteUserRepository;
3743
private final UniversityRepository universityRepository;
3844
private final TermRepository termRepository;
45+
private final MentorApplicationRepository mentorApplicationRepository;
3946

4047
@Transactional(readOnly = true)
4148
public MentorMyPageResponse getMentorMyPage(long siteUserId) {
@@ -61,18 +68,53 @@ public void updateMentorMyPage(long siteUserId, MentorMyPageUpdateRequest reques
6168
updateChannel(request.channels(), mentor);
6269
}
6370

71+
private void updateChannel(List<ChannelRequest> channelRequests, Mentor mentor) {
72+
List<Channel> newChannels = buildChannels(channelRequests);
73+
mentor.updateChannels(newChannels);
74+
}
75+
76+
@Transactional
77+
public void createMentorMyPage(long siteUserId, MentorMyPageCreateRequest request) {
78+
validateUserCanCreateMentor(siteUserId);
79+
validateChannelRegistrationLimit(request.channels());
80+
MentorApplication mentorApplication = mentorApplicationRepository.findBySiteUserIdAndMentorApplicationStatus(siteUserId, MentorApplicationStatus.APPROVED)
81+
.orElseThrow(() -> new CustomException(MENTOR_APPLICATION_NOT_FOUND));
82+
83+
Mentor mentor = new Mentor(
84+
request.introduction(),
85+
request.passTip(),
86+
siteUserId,
87+
mentorApplication.getUniversityId(),
88+
mentorApplication.getTermId()
89+
);
90+
91+
createChannels(request.channels(), mentor);
92+
mentorRepository.save(mentor);
93+
}
94+
95+
private void validateUserCanCreateMentor(long siteUserId) {
96+
if (mentorRepository.existsBySiteUserId(siteUserId)) {
97+
throw new CustomException(MENTOR_ALREADY_EXISTS);
98+
}
99+
}
100+
64101
private void validateChannelRegistrationLimit(List<ChannelRequest> channelRequests) {
65102
if (channelRequests.size() > CHANNEL_REGISTRATION_LIMIT) {
66103
throw new CustomException(CHANNEL_REGISTRATION_LIMIT_EXCEEDED);
67104
}
68105
}
69106

70-
private void updateChannel(List<ChannelRequest> channelRequests, Mentor mentor) {
107+
private void createChannels(List<ChannelRequest> channelRequests, Mentor mentor) {
108+
List<Channel> newChannels = buildChannels(channelRequests);
109+
mentor.createChannels(newChannels);
110+
}
111+
112+
private List<Channel> buildChannels(List<ChannelRequest> channelRequests) {
71113
int sequence = CHANNEL_SEQUENCE_START_NUMBER;
72114
List<Channel> newChannels = new ArrayList<>();
73115
for (ChannelRequest request : channelRequests) {
74116
newChannels.add(new Channel(sequence++, request.type(), request.url()));
75117
}
76-
mentor.updateChannels(newChannels);
118+
return newChannels;
77119
}
78120
}

src/main/java/com/example/solidconnection/term/repository/TermRepository.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,4 +7,6 @@
77
public interface TermRepository extends JpaRepository<Term, Long> {
88

99
Optional<Term> findByIsCurrentTrue();
10+
11+
Optional<Term> findByName(String name);
1012
}

0 commit comments

Comments
 (0)