From df290ef168363726d7e0aa1b15739fc95e00903c Mon Sep 17 00:00:00 2001 From: Kang Tae Seong <69503955+kangkings@users.noreply.github.com> Date: Thu, 4 Jun 2026 11:34:48 +0900 Subject: [PATCH 1/4] =?UTF-8?q?Applicant=20=EB=8F=84=EB=A9=94=EC=9D=B8=20?= =?UTF-8?q?=EA=B8=B0=EB=B0=98=20=EA=B5=AC=EC=A1=B0=20=EC=B6=94=EA=B0=80=20?= =?UTF-8?q?(#571)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * [feat] Applicant 도메인 준비 * [ci] tmp/dev 브랜치에서 ci 활성화 (임시) * [chore] 주석 추가 및 수정 --- .github/workflows/continuous-intergration.yml | 1 + .../common/security/jwt/JwtTokenProvider.java | 8 +- .../controller/ApplicantApiSpec.java | 43 +++ .../controller/ApplicantController.java | 97 +++++++ .../dto/ApplicantAccountProjection.java | 13 + .../domain/applicant/dto/ApplicantDto.java | 42 +++ .../dto/ApplicantProfileResponse.java | 32 +++ .../entity}/Applicant.java | 127 +++++++-- .../applicant/entity/ApplicantEditor.java | 27 ++ .../exception/ApplicantErrorCode.java | 25 ++ .../exception/ApplicantException.java | 10 + .../repository/ApplicantQueryRepository.java | 15 ++ .../ApplicantQueryRepositoryImpl.java | 90 +++++++ .../repository/ApplicantRepository.java | 21 ++ .../applicant/service/ApplicantService.java | 113 ++++++++ .../domain/applicant/ApplicantTest.java | 120 +++++++++ .../controller/ApplicantControllerTest.java | 219 ++++++++++++++++ .../repository/ApplicantRepositoryTest.java | 137 ++++++++++ .../service/ApplicantServiceTest.java | 247 ++++++++++++++++++ 19 files changed, 1359 insertions(+), 28 deletions(-) create mode 100644 src/main/java/org/ject/support/domain/applicant/controller/ApplicantApiSpec.java create mode 100644 src/main/java/org/ject/support/domain/applicant/controller/ApplicantController.java create mode 100644 src/main/java/org/ject/support/domain/applicant/dto/ApplicantAccountProjection.java create mode 100644 src/main/java/org/ject/support/domain/applicant/dto/ApplicantDto.java create mode 100644 src/main/java/org/ject/support/domain/applicant/dto/ApplicantProfileResponse.java rename src/main/java/org/ject/support/domain/{apply/domain => applicant/entity}/Applicant.java (52%) create mode 100644 src/main/java/org/ject/support/domain/applicant/entity/ApplicantEditor.java create mode 100644 src/main/java/org/ject/support/domain/applicant/exception/ApplicantErrorCode.java create mode 100644 src/main/java/org/ject/support/domain/applicant/exception/ApplicantException.java create mode 100644 src/main/java/org/ject/support/domain/applicant/repository/ApplicantQueryRepository.java create mode 100644 src/main/java/org/ject/support/domain/applicant/repository/ApplicantQueryRepositoryImpl.java create mode 100644 src/main/java/org/ject/support/domain/applicant/repository/ApplicantRepository.java create mode 100644 src/main/java/org/ject/support/domain/applicant/service/ApplicantService.java create mode 100644 src/test/java/org/ject/support/domain/applicant/ApplicantTest.java create mode 100644 src/test/java/org/ject/support/domain/applicant/controller/ApplicantControllerTest.java create mode 100644 src/test/java/org/ject/support/domain/applicant/repository/ApplicantRepositoryTest.java create mode 100644 src/test/java/org/ject/support/domain/applicant/service/ApplicantServiceTest.java diff --git a/.github/workflows/continuous-intergration.yml b/.github/workflows/continuous-intergration.yml index f11aac4f..c2d3f69d 100644 --- a/.github/workflows/continuous-intergration.yml +++ b/.github/workflows/continuous-intergration.yml @@ -5,6 +5,7 @@ on: branches: - "main" - "dev" + - "tmp/dev" #Todo: 도메인 분리를 위한 임시 dev, dev로 병합시 제거 permissions: checks: write diff --git a/src/main/java/org/ject/support/common/security/jwt/JwtTokenProvider.java b/src/main/java/org/ject/support/common/security/jwt/JwtTokenProvider.java index 478bf68c..30d04ef1 100644 --- a/src/main/java/org/ject/support/common/security/jwt/JwtTokenProvider.java +++ b/src/main/java/org/ject/support/common/security/jwt/JwtTokenProvider.java @@ -15,6 +15,7 @@ import lombok.RequiredArgsConstructor; import org.ject.support.common.exception.GlobalException; import org.ject.support.common.security.CustomUserDetails; +import org.ject.support.domain.applicant.entity.Applicant; import org.ject.support.domain.member.Role; import org.ject.support.domain.member.entity.Member; import org.springframework.beans.factory.annotation.Value; @@ -185,6 +186,11 @@ public Authentication createAuthenticationByMember(Member member) { CustomUserDetails userDetails = new CustomUserDetails(member); return new UsernamePasswordAuthenticationToken(userDetails, "", userDetails.getAuthorities()); } + + public Authentication createAuthenticationByApplicant(Applicant applicant) { + CustomUserDetails userDetails = new CustomUserDetails(applicant.getEmail(), applicant.getId(), applicant.getRole()); + return new UsernamePasswordAuthenticationToken(userDetails, "", userDetails.getAuthorities()); + } /** * 인증번호 검증이 완료된 사용자를 위한 임시 토큰 발급 @@ -230,4 +236,4 @@ public String extractEmailFromVerificationToken(String token) { } return claims.getSubject(); } -} \ No newline at end of file +} diff --git a/src/main/java/org/ject/support/domain/applicant/controller/ApplicantApiSpec.java b/src/main/java/org/ject/support/domain/applicant/controller/ApplicantApiSpec.java new file mode 100644 index 00000000..17978155 --- /dev/null +++ b/src/main/java/org/ject/support/domain/applicant/controller/ApplicantApiSpec.java @@ -0,0 +1,43 @@ +package org.ject.support.domain.applicant.controller; + +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import jakarta.validation.Valid; +import org.ject.support.common.security.AuthPrincipal; +import org.ject.support.domain.applicant.dto.ApplicantDto; +import org.ject.support.domain.applicant.dto.ApplicantProfileResponse; +import org.springframework.web.bind.annotation.RequestBody; + +@Tag(name = "Applicant", description = "지원자 API") +public interface ApplicantApiSpec { + + @Operation( + summary = "지원자 등록", + description = "PIN 번호를 암호화하여 지원자를 생성합니다. 인증번호 검증 후 발급받은 토큰을 통해 인증된 사용자만 접근 가능합니다.") + boolean registerTempApplicant(HttpServletRequest request, HttpServletResponse response, + @Valid @RequestBody ApplicantDto.RegisterRequest registerRequest); + + @Operation( + summary = "지원자의 최초 정보 등록", + description = "지원자(ROLE_APPLY)가 이름과 전화번호를 처음 등록할 때 사용합니다.") + void registerInitialProfile(@AuthPrincipal Long applicantId, + @Valid @RequestBody ApplicantDto.InitialProfileRequest request); + + @Operation( + summary = "PIN 번호 재설정", + description = "PIN 번호를 재설정합니다.") + void resetPin(@AuthPrincipal Long applicantId, + @Valid @RequestBody ApplicantDto.UpdatePinRequest request); + + @Operation( + summary = "프로필 정보 최초 등록 여부 확인", + description = "지원자의 최초 프로필 정보(이름, 전화번호) 등록 여부를 확인합니다.") + boolean isInitialApplicant(@AuthPrincipal Long applicantId); + + @Operation( + summary = "현재 로그인한 지원자 프로필 조회", + description = "현재 로그인한 지원자의 프로필를 조회합니다.") + ApplicantProfileResponse getCurrentApplicant(@AuthPrincipal Long applicantId); +} diff --git a/src/main/java/org/ject/support/domain/applicant/controller/ApplicantController.java b/src/main/java/org/ject/support/domain/applicant/controller/ApplicantController.java new file mode 100644 index 00000000..96ee143b --- /dev/null +++ b/src/main/java/org/ject/support/domain/applicant/controller/ApplicantController.java @@ -0,0 +1,97 @@ +package org.ject.support.domain.applicant.controller; + +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import jakarta.validation.Valid; +import lombok.RequiredArgsConstructor; +import org.ject.support.common.security.AuthPrincipal; +import org.ject.support.common.security.CustomSuccessHandler; +import org.ject.support.common.security.jwt.JwtTokenProvider; +import org.ject.support.domain.applicant.dto.ApplicantDto; +import org.ject.support.domain.applicant.dto.ApplicantProfileResponse; +import org.ject.support.domain.applicant.service.ApplicantService; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.security.core.Authentication; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.PutMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +@RestController +@RequestMapping("/applicants") +@RequiredArgsConstructor +public class ApplicantController implements ApplicantApiSpec { + + private final ApplicantService applicantService; + private final JwtTokenProvider jwtTokenProvider; + private final CustomSuccessHandler customSuccessHandler; + + + /** + * 지원자 등록 API + * 인증번호 검증 후 발급받은 토큰을 통해 인증된 사용자만 접근 가능합니다. + * PIN 번호를 암호화하여 지원자를 생성합니다. + */ + @Override + @PostMapping("/apply") + @PreAuthorize("hasRole('ROLE_VERIFICATION')") + public boolean registerTempApplicant(HttpServletRequest request, HttpServletResponse response, + @Valid @RequestBody ApplicantDto.RegisterRequest registerRequest) { + + // 쿠키에서 verification 토큰 추출 + String token = jwtTokenProvider.resolveVerificationToken(request); + + // 토큰에서 이메일 추출 + String email = jwtTokenProvider.extractEmailFromVerificationToken(token); + + // 지원자 생성 및 토큰 발급 + Authentication authentication = applicantService.registerTempApplicant(registerRequest, email); + customSuccessHandler.onAuthenticationSuccess(request, response, authentication); + + // verification 토큰은 더 이상 필요 없으므로 삭제 + jwtTokenProvider.deleteVerificationCookie(response); + + return true; + } + + /** + * 지원자의 최초 정보 등록 API + * 지원자(ROLE_APPLY)가 이름과 전화번호를 처음 등록할 때 사용합니다. + */ + @Override + @PutMapping("/profile/initial") + @PreAuthorize("hasRole('ROLE_APPLY')") + public void registerInitialProfile(@AuthPrincipal Long applicantId, + @Valid @RequestBody ApplicantDto.InitialProfileRequest request) { + + // 지원자의 최초 프로필 정보 등록 + applicantService.registerInitialProfile(request, applicantId); + } + + @Override + @PutMapping("/pin") + @PreAuthorize("hasRole('ROLE_APPLY')") + public void resetPin(@AuthPrincipal Long applicantId, + @Valid @RequestBody ApplicantDto.UpdatePinRequest request) { + + // 지원자의 PIN 번호 재설정 + applicantService.updatePin(request, applicantId); + } + + @Override + @GetMapping("/profile/initial/status") + @PreAuthorize("hasRole('ROLE_APPLY')") + public boolean isInitialApplicant(@AuthPrincipal Long applicantId) { + // 지원자의 최초 프로필 정보 등록 여부 확인 + return applicantService.checkIsInitialed(applicantId); + } + + @Override + @GetMapping("/me") + @PreAuthorize("hasRole('ROLE_APPLY') or hasRole('ROLE_SEMESTER')") + public ApplicantProfileResponse getCurrentApplicant(@AuthPrincipal Long applicantId) { + return applicantService.getApplicantProfile(applicantId); + } +} diff --git a/src/main/java/org/ject/support/domain/applicant/dto/ApplicantAccountProjection.java b/src/main/java/org/ject/support/domain/applicant/dto/ApplicantAccountProjection.java new file mode 100644 index 00000000..d9a42bf1 --- /dev/null +++ b/src/main/java/org/ject/support/domain/applicant/dto/ApplicantAccountProjection.java @@ -0,0 +1,13 @@ +package org.ject.support.domain.applicant.dto; + +import org.ject.support.domain.member.MemberStatus; +import org.ject.support.domain.member.Role; + +public record ApplicantAccountProjection( + Long id, + String email, + String name, + Role role, + MemberStatus status +) { +} diff --git a/src/main/java/org/ject/support/domain/applicant/dto/ApplicantDto.java b/src/main/java/org/ject/support/domain/applicant/dto/ApplicantDto.java new file mode 100644 index 00000000..110520bc --- /dev/null +++ b/src/main/java/org/ject/support/domain/applicant/dto/ApplicantDto.java @@ -0,0 +1,42 @@ +package org.ject.support.domain.applicant.dto; + +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.Pattern; +import org.ject.support.domain.applicant.entity.Applicant; +import org.ject.support.domain.member.MemberStatus; +import org.ject.support.domain.member.Role; + +public class ApplicantDto { + + public record RegisterRequest( + + @NotBlank @Pattern(regexp = "^\\d{6}$", message = "PIN 번호는 6자리 숫자여야 합니다.") String pin) { + + public Applicant toEntity(Long semesterId, String email, String encodedPin) { + return Applicant.builder() + .semesterId(semesterId) + .email(email) + .pin(encodedPin) + .status(MemberStatus.ACTIVE) + .role(Role.APPLY) + .build(); + } + } + + /** + * 지원자가 최초로 이름과 전화번호를 등록할 때 사용하는 DTO + */ + public record InitialProfileRequest( + + @NotBlank @Pattern(regexp = "^[가-힣]{1,5}$", message = "한글 1~5글자만 입력 가능합니다.") + String name, + + @NotBlank @Pattern(regexp = "^010\\d{8}$", message = "010으로 시작하는 11자리 숫자를 입력하세요.") + String phoneNumber) { + + } + + public record UpdatePinRequest( + @NotBlank @Pattern(regexp = "^\\d{6}$", message = "PIN 번호는 6자리 숫자여야 합니다.") String pin) { + } +} diff --git a/src/main/java/org/ject/support/domain/applicant/dto/ApplicantProfileResponse.java b/src/main/java/org/ject/support/domain/applicant/dto/ApplicantProfileResponse.java new file mode 100644 index 00000000..f816f180 --- /dev/null +++ b/src/main/java/org/ject/support/domain/applicant/dto/ApplicantProfileResponse.java @@ -0,0 +1,32 @@ +package org.ject.support.domain.applicant.dto; + +import lombok.Builder; +import org.ject.support.domain.applicant.entity.Applicant; +import org.ject.support.domain.member.CareerDetails; +import org.ject.support.domain.member.ExperiencePeriod; +import org.ject.support.domain.member.Region; + +import java.util.List; + +@Builder +public record ApplicantProfileResponse( + Long id, + String name, + String phoneNumber, + CareerDetails careerDetails, + Region region, + ExperiencePeriod experiencePeriod, + List interestedDomains +) { + public static ApplicantProfileResponse of(Applicant applicant) { + return ApplicantProfileResponse.builder() + .id(applicant.getId()) + .name(applicant.getName()) + .phoneNumber(applicant.getPhoneNumber()) + .careerDetails(applicant.getCareerDetails()) + .region(applicant.getRegion()) + .experiencePeriod(applicant.getExperiencePeriod()) + .interestedDomains(applicant.getInterestedDomains()) + .build(); + } +} diff --git a/src/main/java/org/ject/support/domain/apply/domain/Applicant.java b/src/main/java/org/ject/support/domain/applicant/entity/Applicant.java similarity index 52% rename from src/main/java/org/ject/support/domain/apply/domain/Applicant.java rename to src/main/java/org/ject/support/domain/applicant/entity/Applicant.java index c371917c..7c406296 100644 --- a/src/main/java/org/ject/support/domain/apply/domain/Applicant.java +++ b/src/main/java/org/ject/support/domain/applicant/entity/Applicant.java @@ -1,4 +1,4 @@ -package org.ject.support.domain.apply.domain; +package org.ject.support.domain.applicant.entity; import jakarta.persistence.Column; import jakarta.persistence.Convert; @@ -9,10 +9,13 @@ import jakarta.persistence.GenerationType; import jakarta.persistence.Id; import jakarta.persistence.Table; -import java.util.ArrayList; -import java.util.List; - -import lombok.*; +import jakarta.validation.constraints.Pattern; +import jakarta.validation.constraints.Size; +import lombok.AccessLevel; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Getter; +import lombok.NoArgsConstructor; import org.hibernate.annotations.SQLDelete; import org.hibernate.annotations.SQLRestriction; import org.ject.support.common.util.StringListConverter; @@ -25,6 +28,10 @@ import org.ject.support.domain.member.Region; import org.ject.support.domain.member.Role; +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; + @Entity @Getter @Builder @@ -39,37 +46,29 @@ public class Applicant extends BaseTimeEntity { @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; + @Column(length = 30, nullable = false) + private String email; + @Column(length = 20) + @Size(max = 20) private String name; @Column(length = 12) + @Pattern(regexp = "^010\\d{8}$", message = "010으로 시작하는 11자리 숫자를 입력하세요.") private String phoneNumber; - @Column(length = 30, nullable = false) - private String email; - - @Column(nullable = false) - private Long semesterId; - @Enumerated(EnumType.STRING) @Column(columnDefinition = "varchar(45)") private JobFamily jobFamily; @Enumerated(EnumType.STRING) - @Column(columnDefinition = "varchar(10)", nullable = false) - private Role role; - - @Column(length = 255) - private String pin; - - @Enumerated(EnumType.STRING) - @Column(columnDefinition = "varchar(45)", nullable = false) + @Column(name = "member_type", columnDefinition = "varchar(45)", nullable = false) @Builder.Default - private MemberStatus status = MemberStatus.ACTIVE; + private MemberType memberType = MemberType.SEMESTER; - @Column(name = "is_deleted", nullable = false) - @Builder.Default - private Boolean isDeleted = false; + @Enumerated(EnumType.STRING) + @Column(length = 30) + private Region region; @Enumerated(EnumType.STRING) @Column(length = 30) @@ -84,12 +83,86 @@ public class Applicant extends BaseTimeEntity { @Builder.Default private List interestedDomains = new ArrayList<>(); + @Column(nullable = false) + private Long semesterId; + @Enumerated(EnumType.STRING) - @Column(length = 30) - private Region region; + @Column(columnDefinition = "varchar(10)", nullable = false) + private Role role; + + @Column(length = 255) + private String pin; + + @Column(name = "is_deleted", nullable = false) + @Builder.Default + private Boolean isDeleted = false; @Enumerated(EnumType.STRING) - @Column(name = "member_type", columnDefinition = "varchar(45)", nullable = false) + @Column(columnDefinition = "varchar(45)", nullable = false) @Builder.Default - private MemberType memberType = MemberType.SEMESTER; + private MemberStatus status = MemberStatus.ACTIVE; + + public void updateNameAndPhoneNumber(String name, String phoneNumber) { + this.name = name; + this.phoneNumber = phoneNumber; + } + + public void updatePin(String pin) { + this.pin = pin; + } + + public boolean isInitialed() { + return this.name != null && this.phoneNumber != null; + } + + public ApplicantEditor.ApplicantEditorBuilder toEditor() { + return ApplicantEditor.builder() + .name(this.name) + .phoneNumber(this.phoneNumber) + .email(this.email) + .semesterId(this.semesterId) + .jobFamily(this.jobFamily) + .region(this.region) + .experiencePeriod(this.experiencePeriod) + .careerDetails(this.careerDetails) + .interestedDomains(this.interestedDomains) + .role(this.role) + .status(this.status); + } + + public void edit(ApplicantEditor editor) { + this.name = editor.name(); + this.phoneNumber = editor.phoneNumber(); + this.email = editor.email(); + this.semesterId = editor.semesterId(); + this.jobFamily = editor.jobFamily(); + this.region = editor.region(); + this.experiencePeriod = editor.experiencePeriod(); + this.careerDetails = editor.careerDetails(); + this.interestedDomains = editor.interestedDomains(); + this.role = editor.role(); + this.status = editor.status(); + } + + public void deleteProfile() { + this.name = null; + this.phoneNumber = null; + this.jobFamily = null; + this.region = null; + this.careerDetails = null; + this.experiencePeriod = null; + this.interestedDomains.clear(); + } + + public void promoteToSemester() { + this.role = Role.SEMESTER; + } + + public void updateMemberType(MemberType memberType) { + this.memberType = Objects.requireNonNull(memberType, "memberType must not be null"); + } + + public boolean isProfileComplete() { + return this.name != null && this.phoneNumber != null; + } } diff --git a/src/main/java/org/ject/support/domain/applicant/entity/ApplicantEditor.java b/src/main/java/org/ject/support/domain/applicant/entity/ApplicantEditor.java new file mode 100644 index 00000000..7fc801aa --- /dev/null +++ b/src/main/java/org/ject/support/domain/applicant/entity/ApplicantEditor.java @@ -0,0 +1,27 @@ +package org.ject.support.domain.applicant.entity; + +import lombok.Builder; +import org.ject.support.domain.member.CareerDetails; +import org.ject.support.domain.member.ExperiencePeriod; +import org.ject.support.domain.member.JobFamily; +import org.ject.support.domain.member.MemberStatus; +import org.ject.support.domain.member.Region; +import org.ject.support.domain.member.Role; + +import java.util.List; + +@Builder +public record ApplicantEditor( + String name, + String phoneNumber, + String email, + Long semesterId, + JobFamily jobFamily, + Region region, + ExperiencePeriod experiencePeriod, + CareerDetails careerDetails, + List interestedDomains, + Role role, + MemberStatus status +) { +} diff --git a/src/main/java/org/ject/support/domain/applicant/exception/ApplicantErrorCode.java b/src/main/java/org/ject/support/domain/applicant/exception/ApplicantErrorCode.java new file mode 100644 index 00000000..ea910ddf --- /dev/null +++ b/src/main/java/org/ject/support/domain/applicant/exception/ApplicantErrorCode.java @@ -0,0 +1,25 @@ +package org.ject.support.domain.applicant.exception; + +import lombok.AllArgsConstructor; +import lombok.Getter; +import org.ject.support.common.exception.ErrorCode; +import org.springframework.http.HttpStatus; + +import static org.springframework.http.HttpStatus.CONFLICT; +import static org.springframework.http.HttpStatus.NOT_FOUND; +import static org.springframework.http.HttpStatus.PAYLOAD_TOO_LARGE; + +@Getter +@AllArgsConstructor +public enum ApplicantErrorCode implements ErrorCode { + NOT_FOUND_APPLICANT(NOT_FOUND, "APPLICANT-1", "지원자를 찾을 수 없습니다."), + ALREADY_EXIST_APPLICANT(CONFLICT, "APPLICANT-2", "이미 가입되어 있는 지원자입니다."), + NOT_FOUND_SEMESTER_OF_APPLICANT(NOT_FOUND, "APPLICANT-3", "지원자의 기수를 찾을 수 없습니다."), + DUPLICATE_EMAIL(CONFLICT, "APPLICANT-4", "이미 사용 중인 이메일입니다."), + EXCEEDED_INTERESTED_DOMAINS_MAX_SIZE(PAYLOAD_TOO_LARGE, "APPLICANT-5", "관심 도메인 목록의 최대 크기를 초과했습니다.") + ; + + private final HttpStatus httpStatus; + private final String code; + private final String message; +} diff --git a/src/main/java/org/ject/support/domain/applicant/exception/ApplicantException.java b/src/main/java/org/ject/support/domain/applicant/exception/ApplicantException.java new file mode 100644 index 00000000..43c57593 --- /dev/null +++ b/src/main/java/org/ject/support/domain/applicant/exception/ApplicantException.java @@ -0,0 +1,10 @@ +package org.ject.support.domain.applicant.exception; + +import org.ject.support.common.exception.BusinessException; +import org.ject.support.common.exception.ErrorCode; + +public class ApplicantException extends BusinessException { + public ApplicantException(final ErrorCode errorCode) { + super(errorCode); + } +} diff --git a/src/main/java/org/ject/support/domain/applicant/repository/ApplicantQueryRepository.java b/src/main/java/org/ject/support/domain/applicant/repository/ApplicantQueryRepository.java new file mode 100644 index 00000000..4d2ac6ac --- /dev/null +++ b/src/main/java/org/ject/support/domain/applicant/repository/ApplicantQueryRepository.java @@ -0,0 +1,15 @@ +package org.ject.support.domain.applicant.repository; + +import org.ject.support.admin.account.dto.AdminAccountSearchCondition; +import org.ject.support.domain.applicant.dto.ApplicantAccountProjection; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; + +import java.util.List; + +public interface ApplicantQueryRepository { + + List findEmailsByIdsAndNotSubmitted(List applicantIds); + + Page findAccounts(AdminAccountSearchCondition condition, Pageable pageable); +} diff --git a/src/main/java/org/ject/support/domain/applicant/repository/ApplicantQueryRepositoryImpl.java b/src/main/java/org/ject/support/domain/applicant/repository/ApplicantQueryRepositoryImpl.java new file mode 100644 index 00000000..92ec6d26 --- /dev/null +++ b/src/main/java/org/ject/support/domain/applicant/repository/ApplicantQueryRepositoryImpl.java @@ -0,0 +1,90 @@ +package org.ject.support.domain.applicant.repository; + +import static org.ject.support.domain.applicant.entity.QApplicant.applicant; +import static org.ject.support.domain.apply.domain.ApplyStatus.SUBMITTED; +import static org.ject.support.domain.apply.domain.QApply.apply; + +import com.querydsl.core.types.Projections; +import com.querydsl.core.types.dsl.BooleanExpression; +import com.querydsl.jpa.JPQLQueryFactory; +import java.util.List; +import java.util.Optional; +import lombok.RequiredArgsConstructor; +import org.ject.support.admin.account.dto.AdminAccountSearchCondition; +import org.ject.support.common.data.PageResponse; +import org.ject.support.domain.applicant.dto.ApplicantAccountProjection; +import org.ject.support.domain.member.MemberStatus; +import org.ject.support.domain.member.Role; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; +import org.springframework.stereotype.Repository; + +@Repository +@RequiredArgsConstructor +public class ApplicantQueryRepositoryImpl implements ApplicantQueryRepository { + + private final JPQLQueryFactory queryFactory; + + @Override + public List findEmailsByIdsAndNotSubmitted(List applicantIds) { + return queryFactory.select(applicant.email) + .from(applicant) + .join(apply) + .on(applicant.id.eq(apply.member.id)) + .where(applicant.id.in(applicantIds), + applicant.isDeleted.eq(false), + apply.status.eq(SUBMITTED).not()) + .fetch(); + } + + @Override + public Page findAccounts(final AdminAccountSearchCondition condition, + final Pageable pageable) { + List content = queryFactory + .select(Projections.constructor( + ApplicantAccountProjection.class, + applicant.id, + applicant.email, + applicant.name, + applicant.role, + applicant.status)) + .from(applicant) + .where( + applicant.isDeleted.eq(false), + applicant.role.in(Role.backofficeRoles()), + inRoles(condition.roles()), + inStatuses(condition.statuses()) + ) + .orderBy(applicant.createdAt.desc(), applicant.id.desc()) + .offset(pageable.getOffset()) + .limit(pageable.getPageSize()) + .fetch(); + + Long total = queryFactory + .select(applicant.count()) + .from(applicant) + .where( + applicant.isDeleted.eq(false), + applicant.role.in(Role.backofficeRoles()), + inRoles(condition.roles()), + inStatuses(condition.statuses()) + ) + .fetchOne(); + + return PageResponse.from(content, pageable, total != null ? total : 0L); + } + + private BooleanExpression inRoles(final List roles) { + return Optional.ofNullable(roles) + .filter(values -> !values.isEmpty()) + .map(applicant.role::in) + .orElse(null); + } + + private BooleanExpression inStatuses(final List statuses) { + return Optional.ofNullable(statuses) + .filter(values -> !values.isEmpty()) + .map(applicant.status::in) + .orElse(null); + } +} diff --git a/src/main/java/org/ject/support/domain/applicant/repository/ApplicantRepository.java b/src/main/java/org/ject/support/domain/applicant/repository/ApplicantRepository.java new file mode 100644 index 00000000..a65ffcfa --- /dev/null +++ b/src/main/java/org/ject/support/domain/applicant/repository/ApplicantRepository.java @@ -0,0 +1,21 @@ +package org.ject.support.domain.applicant.repository; + +import org.ject.support.domain.applicant.entity.Applicant; +import org.ject.support.domain.member.Role; +import org.springframework.data.jpa.repository.JpaRepository; + +import java.util.Collection; +import java.util.Optional; + +public interface ApplicantRepository extends JpaRepository, ApplicantQueryRepository { + + Optional findByEmail(String email); + + Optional findByEmailAndRole(String email, Role role); + + Optional findByEmailAndRoleIn(String email, Collection roles); + + Optional findByIdAndRoleIn(Long id, Collection roles); + + boolean existsByEmail(String email); +} diff --git a/src/main/java/org/ject/support/domain/applicant/service/ApplicantService.java b/src/main/java/org/ject/support/domain/applicant/service/ApplicantService.java new file mode 100644 index 00000000..6a448f28 --- /dev/null +++ b/src/main/java/org/ject/support/domain/applicant/service/ApplicantService.java @@ -0,0 +1,113 @@ +package org.ject.support.domain.applicant.service; + +import lombok.RequiredArgsConstructor; +import org.ject.support.common.security.jwt.JwtTokenProvider; +import org.ject.support.domain.applicant.dto.ApplicantDto; +import org.ject.support.domain.applicant.dto.ApplicantDto.RegisterRequest; +import org.ject.support.domain.applicant.dto.ApplicantDto.UpdatePinRequest; +import org.ject.support.domain.applicant.dto.ApplicantProfileResponse; +import org.ject.support.domain.applicant.entity.Applicant; +import org.ject.support.domain.applicant.exception.ApplicantErrorCode; +import org.ject.support.domain.applicant.exception.ApplicantException; +import org.ject.support.domain.applicant.repository.ApplicantRepository; +import org.ject.support.domain.recruit.domain.Semester; +import org.ject.support.domain.recruit.exception.SemesterErrorCode; +import org.ject.support.domain.recruit.exception.SemesterException; +import org.ject.support.domain.recruit.repository.SemesterRepository; +import org.springframework.security.core.Authentication; +import org.springframework.security.crypto.password.PasswordEncoder; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import static org.ject.support.domain.applicant.exception.ApplicantErrorCode.ALREADY_EXIST_APPLICANT; + +@Service +@RequiredArgsConstructor +public class ApplicantService { + + private final ApplicantRepository applicantRepository; + private final SemesterRepository semesterRepository; + private final JwtTokenProvider jwtTokenProvider; + private final PasswordEncoder passwordEncoder; + + /** + * 인증된 사용자의 PIN 번호를 설정하고 지원자를 생성합니다. + * 인증번호 검증 -> PIN 번호 설정 -> 지원자 생성 단계에서 호출됩니다. + * 인증 토큰을 통해 검증된 이메일로 지원자를 조회하고, PIN 번호를 암호화하여 저장합니다. + */ + @Transactional + public Authentication registerTempApplicant(RegisterRequest registerRequest, String email) { + // 이메일로 지원자 조회 + /** + * Todo: 이메일을 단건으로 구분할 수 있는 정책 설정 + * 기존 Member는 email에 유니크 제약조건 존재, applicant는 이력 의미도 포함하기 때문에 유니크 제약 X + * 여러 기수에 지원한 경우 현재 로직대로면 복수 행 응답 가능 + * 지원데이터는 모집 공고 당 1개만 존재할 수 있음 -> 이메일 조회를 이메일+공고로 조회 + * applicant에 recruit_id 필요 (값 참조) + */ + Applicant applicant = applicantRepository.findByEmail(email) + .orElse(null); + + if (applicant == null) { + // 새로운 지원자 생성 + applicant = createTempApplicantWithPin(registerRequest, email); + } else { + // 기존 지원자가 있는 경우 PIN 번호만 업데이트 + throw new ApplicantException(ALREADY_EXIST_APPLICANT); + } + + // 인증 및 토큰 발급 + return jwtTokenProvider.createAuthenticationByApplicant(applicant); + } + + /** + * PIN 번호가 설정된 지원자를 생성합니다. + * PIN 번호는 암호화하여 저장합니다. + */ + private Applicant createTempApplicantWithPin(RegisterRequest registerRequest, String email) { + String encodedPin = passwordEncoder.encode(registerRequest.pin()); + + Semester semester = semesterRepository.findRecruitingSemester() + .orElseThrow(() -> new SemesterException(SemesterErrorCode.NOT_FOUND_RECRUITING_SEMESTER)); + + Applicant applicant = registerRequest.toEntity(semester.getId(), email, encodedPin); + + return applicantRepository.save(applicant); + } + + /** + * 지원자의 최초 프로필 정보(이름, 전화번호) 등록 + * 지원자(ROLE_APPLY)가 최초로 이름과 전화번호를 등록할 때 호출됩니다. + */ + @Transactional + public void registerInitialProfile(ApplicantDto.InitialProfileRequest request, Long applicantId) { + Applicant applicant = applicantRepository.findById(applicantId) + .orElseThrow(() -> new ApplicantException(ApplicantErrorCode.NOT_FOUND_APPLICANT)); + + applicant.updateNameAndPhoneNumber(request.name(), request.phoneNumber()); + } + + @Transactional + public void updatePin(UpdatePinRequest request, Long applicantId) { + Applicant applicant = applicantRepository.findById(applicantId) + .orElseThrow(() -> new ApplicantException(ApplicantErrorCode.NOT_FOUND_APPLICANT)); + + String encodedPin = passwordEncoder.encode(request.pin()); + applicant.updatePin(encodedPin); + } + + @Transactional(readOnly = true) + public boolean checkIsInitialed(Long applicantId) { + Applicant applicant = applicantRepository.findById(applicantId) + .orElseThrow(() -> new ApplicantException(ApplicantErrorCode.NOT_FOUND_APPLICANT)); + + return applicant.isInitialed(); + } + + @Transactional(readOnly = true) + public ApplicantProfileResponse getApplicantProfile(Long applicantId) { + Applicant applicant = applicantRepository.findById(applicantId) + .orElseThrow(() -> new ApplicantException(ApplicantErrorCode.NOT_FOUND_APPLICANT)); + return ApplicantProfileResponse.of(applicant); + } +} diff --git a/src/test/java/org/ject/support/domain/applicant/ApplicantTest.java b/src/test/java/org/ject/support/domain/applicant/ApplicantTest.java new file mode 100644 index 00000000..13f9361f --- /dev/null +++ b/src/test/java/org/ject/support/domain/applicant/ApplicantTest.java @@ -0,0 +1,120 @@ +package org.ject.support.domain.applicant; + +import org.ject.support.domain.applicant.entity.Applicant; +import org.ject.support.domain.member.JobFamily; +import org.ject.support.domain.member.MemberStatus; +import org.ject.support.domain.member.MemberType; +import org.ject.support.domain.member.Region; +import org.ject.support.domain.member.Role; +import org.junit.jupiter.api.Test; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +class ApplicantTest { + + @Test + void 지원자_엔티티_생성에_성공() { + // given + String name = "John Doe"; + String phoneNumber = "01012345678"; + String email = "john@example.com"; + JobFamily jobFamily = JobFamily.BE; + Role role = Role.SEMESTER; + Region region = Region.SEOUL; + + // when + Applicant applicant = Applicant.builder() + .name(name) + .phoneNumber(phoneNumber) + .email(email) + .jobFamily(jobFamily) + .role(role) + .region(region) + .status(MemberStatus.ACTIVE) + .build(); + + // then + assertThat(applicant.getName()).isEqualTo(name); + assertThat(applicant.getPhoneNumber()).isEqualTo(phoneNumber); + assertThat(applicant.getEmail()).isEqualTo(email); + assertThat(applicant.getJobFamily()).isEqualTo(jobFamily); + assertThat(applicant.getMemberType()).isEqualTo(MemberType.SEMESTER); + assertThat(applicant.getRole()).isEqualTo(role); + assertThat(applicant.getRegion()).isEqualTo(region); + } + + @Test + void 지원자_엔티티_최소_필수_필드로_생성에_성공() { + // given + String name = "John Doe"; + String phoneNumber = "01012345678"; + String email = "john@example.com"; + Role role = Role.SEMESTER; + Region region = Region.OVERSEAS; + + // when + Applicant applicant = Applicant.builder() + .name(name) + .phoneNumber(phoneNumber) + .email(email) + .role(role) + .region(region) + .status(MemberStatus.ACTIVE) + .build(); + + // then + assertThat(applicant.getName()).isEqualTo(name); + assertThat(applicant.getPhoneNumber()).isEqualTo(phoneNumber); + assertThat(applicant.getEmail()).isEqualTo(email); + assertThat(applicant.getRole()).isEqualTo(role); + assertThat(applicant.getRegion()).isEqualTo(region); + assertThat(applicant.getJobFamily()).isNull(); + assertThat(applicant.getMemberType()).isEqualTo(MemberType.SEMESTER); + } + + @Test + void 구성원_타입을_null로_변경하면_예외가_발생한다() { + // given + Applicant applicant = Applicant.builder() + .email("john@example.com") + .role(Role.SEMESTER) + .status(MemberStatus.ACTIVE) + .build(); + + // when, then + assertThatThrownBy(() -> applicant.updateMemberType(null)) + .isInstanceOf(NullPointerException.class) + .hasMessage("memberType must not be null"); + } + + @Test + void 지원자의_이름이_없을_경우_프로필_완성_확인에_FALSE_반환() { + // given + Applicant applicant = Applicant.builder() + .phoneNumber("01012345678") + .email("noName@ject.com") + .build(); + + // when + boolean result = applicant.isProfileComplete(); + + // then + assertThat(result).isFalse(); + } + + @Test + void 지원자의_이름과_핸드폰번호가_있을_경우_프로필_완성_확인에_TRUE_반환() { + // given + Applicant applicant = Applicant.builder() + .name("이름") + .phoneNumber("01012345678") + .build(); + + // when + boolean result = applicant.isProfileComplete(); + + // then + assertThat(result).isTrue(); + } +} diff --git a/src/test/java/org/ject/support/domain/applicant/controller/ApplicantControllerTest.java b/src/test/java/org/ject/support/domain/applicant/controller/ApplicantControllerTest.java new file mode 100644 index 00000000..7631d181 --- /dev/null +++ b/src/test/java/org/ject/support/domain/applicant/controller/ApplicantControllerTest.java @@ -0,0 +1,219 @@ +package org.ject.support.domain.applicant.controller; + +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.BDDMockito.given; +import static org.mockito.Mockito.doNothing; +import static org.mockito.Mockito.lenient; +import static org.mockito.Mockito.verify; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.put; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; +import static org.springframework.test.web.servlet.setup.MockMvcBuilders.standaloneSetup; + +import com.fasterxml.jackson.databind.ObjectMapper; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import org.ject.support.common.security.CustomSuccessHandler; +import org.ject.support.common.security.CustomUserDetails; +import org.ject.support.common.security.jwt.JwtTokenProvider; +import org.ject.support.domain.applicant.dto.ApplicantDto.InitialProfileRequest; +import org.ject.support.domain.applicant.dto.ApplicantDto.RegisterRequest; +import org.ject.support.domain.applicant.dto.ApplicantDto.UpdatePinRequest; +import org.ject.support.domain.applicant.service.ApplicantService; +import org.ject.support.domain.member.Role; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; +import org.springframework.http.MediaType; +import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.test.web.servlet.MockMvc; + +@ExtendWith(MockitoExtension.class) +class ApplicantControllerTest { + + @InjectMocks + private ApplicantController applicantController; + + @Mock + private ApplicantService applicantService; + + @Mock + private JwtTokenProvider jwtTokenProvider; + + @Mock + private CustomSuccessHandler customSuccessHandler; + + private MockMvc mockMvc; + private ObjectMapper objectMapper; + + private final String TEST_NAME = "홍길동"; + private final String TEST_EMAIL = "test@example.com"; + private final String TEST_PHONE_NUMBER = "01012345678"; + private final String TEST_PIN = "123456"; + private final String TEST_VERIFICATION_TOKEN = "test.verification.token"; + + @BeforeEach + void setUp() { + objectMapper = new ObjectMapper(); + mockMvc = standaloneSetup(applicantController).build(); + } + + @Test + void 지원자_등록_성공() throws Exception { + // given + RegisterRequest request = new RegisterRequest(TEST_PIN); + Authentication mockAuthentication = new UsernamePasswordAuthenticationToken( + new CustomUserDetails(TEST_EMAIL, 1L, Role.APPLY), "", null); + + given(jwtTokenProvider.resolveVerificationToken(any())).willReturn(TEST_VERIFICATION_TOKEN); + given(jwtTokenProvider.extractEmailFromVerificationToken(TEST_VERIFICATION_TOKEN)).willReturn(TEST_EMAIL); + given(applicantService.registerTempApplicant(any(RegisterRequest.class), anyString())).willReturn(mockAuthentication); + + // customSuccessHandler.onAuthenticationSuccess 메소드 호출 모킹 + doNothing().when(customSuccessHandler).onAuthenticationSuccess(any(HttpServletRequest.class), any( + HttpServletResponse.class), any(Authentication.class)); + + // when & then + mockMvc.perform(post("/applicants/apply") + .cookie(new jakarta.servlet.http.Cookie("verificationToken", TEST_VERIFICATION_TOKEN)) + .contentType(MediaType.APPLICATION_JSON) + .content(objectMapper.writeValueAsString(request))) + .andExpect(status().isOk()); + + verify(jwtTokenProvider).resolveVerificationToken(any()); + verify(jwtTokenProvider).extractEmailFromVerificationToken(TEST_VERIFICATION_TOKEN); + verify(applicantService).registerTempApplicant(any(RegisterRequest.class), eq(TEST_EMAIL)); + verify(customSuccessHandler).onAuthenticationSuccess(any(HttpServletRequest.class), + any(HttpServletResponse.class), eq(mockAuthentication)); + } + + @Test + void 지원자_최초_프로필_등록_성공() throws Exception { + // given + InitialProfileRequest request = new InitialProfileRequest(TEST_NAME, TEST_PHONE_NUMBER); + Long applicantId = 1L; + + // lenient 설정을 사용하여 엄격한 스텔빙 검사를 해제 + lenient().doNothing().when(applicantService).registerInitialProfile(any(), eq(applicantId)); + + // CustomUserDetails를 사용하여 인증 정보 설정 (ROLE_APPLY 권한) + CustomUserDetails userDetails = new CustomUserDetails(TEST_EMAIL, applicantId, Role.APPLY); + Authentication auth = new UsernamePasswordAuthenticationToken(userDetails, "", userDetails.getAuthorities()); + SecurityContextHolder.getContext().setAuthentication(auth); + + // when & then + mockMvc.perform(put("/applicants/profile/initial") + .contentType(MediaType.APPLICATION_JSON) + .content(objectMapper.writeValueAsString(request))) + .andExpect(status().isOk()); + + // verify를 사용하지 않고 스텔빙만 확인 + // 테스트 후 인증 정보 초기화 + SecurityContextHolder.clearContext(); + } + + @Test + void 핀번호_재설정_성공() throws Exception { + // given + UpdatePinRequest request = new UpdatePinRequest("654321"); // 새로운 PIN 번호 + Long applicantId = 1L; + + // lenient 설정을 사용하여 엄격한 스텔빙 검사를 해제 + lenient().doNothing().when(applicantService).updatePin(any(UpdatePinRequest.class), eq(applicantId)); + + // CustomUserDetails를 사용하여 인증 정보 설정 (ROLE_APPLY 권한) + CustomUserDetails userDetails = new CustomUserDetails(TEST_EMAIL, applicantId, Role.APPLY); + Authentication auth = new UsernamePasswordAuthenticationToken(userDetails, "", userDetails.getAuthorities()); + SecurityContextHolder.getContext().setAuthentication(auth); + + // when & then + mockMvc.perform(put("/applicants/pin") + .contentType(MediaType.APPLICATION_JSON) + .content(objectMapper.writeValueAsString(request))) + .andExpect(status().isOk()); + + // verify를 사용하지 않고 스텔빙만 확인 + // 테스트 후 인증 정보 초기화 + SecurityContextHolder.clearContext(); + } + + @Test + void 핀번호_재설정_실패_유효하지_않은_PIN_번호_형식() throws Exception { + // given + // 유효하지 않은 PIN 번호 (6자리가 아님) + String invalidPin = "12345"; + UpdatePinRequest request = new UpdatePinRequest(invalidPin); + Long applicantId = 1L; + + // CustomUserDetails를 사용하여 인증 정보 설정 (ROLE_APPLY 권한) + CustomUserDetails userDetails = new CustomUserDetails(TEST_EMAIL, applicantId, Role.APPLY); + Authentication auth = new UsernamePasswordAuthenticationToken(userDetails, "", userDetails.getAuthorities()); + SecurityContextHolder.getContext().setAuthentication(auth); + + // when & then + mockMvc.perform(put("/applicants/pin") + .contentType(MediaType.APPLICATION_JSON) + .content(objectMapper.writeValueAsString(request))) + .andExpect(status().isBadRequest()); + + // 테스트 후 인증 정보 초기화 + SecurityContextHolder.clearContext(); + } + + @Test + void 지원자_최초_프로필_등록_여부_확인_성공_등록된_경우() throws Exception { + // given + Long applicantId = 1L; + + // 모킹 설정을 lenient로 변경하여 엄격한 스텁 검사를 비활성화 + lenient().when(applicantService.checkIsInitialed(any())).thenReturn(true); + + // CustomUserDetails를 사용하여 인증 정보 설정 (ROLE_APPLY 권한) + CustomUserDetails userDetails = new CustomUserDetails(TEST_EMAIL, applicantId, Role.APPLY); + Authentication auth = new UsernamePasswordAuthenticationToken(userDetails, "", userDetails.getAuthorities()); + SecurityContextHolder.getContext().setAuthentication(auth); + + // when & then + mockMvc.perform(org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get( + "/applicants/profile/initial/status") + .contentType(MediaType.APPLICATION_JSON) + .principal(auth)) + .andExpect(status().isOk()) + .andExpect(org.springframework.test.web.servlet.result.MockMvcResultMatchers.content().string("true")); + + // 테스트 후 인증 정보 초기화 + SecurityContextHolder.clearContext(); + } + + @Test + void 지원자_최초_프로필_등록_여부_확인_성공_미등록된_경우() throws Exception { + // given + Long applicantId = 1L; + + // 모킹 설정을 lenient로 변경하여 엄격한 스텁 검사를 비활성화 + lenient().when(applicantService.checkIsInitialed(any())).thenReturn(false); + + // CustomUserDetails를 사용하여 인증 정보 설정 (ROLE_APPLY 권한) + CustomUserDetails userDetails = new CustomUserDetails(TEST_EMAIL, applicantId, Role.APPLY); + Authentication auth = new UsernamePasswordAuthenticationToken(userDetails, "", userDetails.getAuthorities()); + SecurityContextHolder.getContext().setAuthentication(auth); + + // when & then + mockMvc.perform(org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get( + "/applicants/profile/initial/status") + .contentType(MediaType.APPLICATION_JSON) + .principal(auth)) + .andExpect(status().isOk()) + .andExpect(org.springframework.test.web.servlet.result.MockMvcResultMatchers.content().string("false")); + + // 테스트 후 인증 정보 초기화 + SecurityContextHolder.clearContext(); + } +} diff --git a/src/test/java/org/ject/support/domain/applicant/repository/ApplicantRepositoryTest.java b/src/test/java/org/ject/support/domain/applicant/repository/ApplicantRepositoryTest.java new file mode 100644 index 00000000..00494a15 --- /dev/null +++ b/src/test/java/org/ject/support/domain/applicant/repository/ApplicantRepositoryTest.java @@ -0,0 +1,137 @@ +package org.ject.support.domain.applicant.repository; + +import org.ject.support.domain.applicant.entity.Applicant; +import org.ject.support.domain.member.JobFamily; +import org.ject.support.domain.member.MemberStatus; +import org.ject.support.domain.member.Role; +import org.ject.support.testconfig.QueryDslTestConfig; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest; +import org.springframework.context.annotation.Import; + +import java.util.List; +import java.util.Optional; + +import static org.assertj.core.api.AssertionsForClassTypes.assertThat; + +@Import(QueryDslTestConfig.class) +@DataJpaTest +class ApplicantRepositoryTest { + + @Autowired + ApplicantRepository applicantRepository; + + @Test + void 이메일과_역활로_지원자를_조회시_존재하는_지원자를_반환한다() { + // given + String email = "test@example.com"; + Role role = Role.ADMIN; + Applicant applicant = createApplicant("테스트", "01012345678", email, JobFamily.FE, role); + applicantRepository.save(applicant); + + // when + Optional found = applicantRepository.findByEmailAndRole(email, role); + + // then + assertThat(found).isPresent(); + assertThat(found.get().getEmail()).isEqualTo(email); + assertThat(found.get().getRole()).isEqualTo(role); + } + + @Test + void 존재하지_않는_이메일과_역활로_지원자를_조회시_빈_Optional을_반환한다() { + // given + String findEmail = "notfound@example.com"; + Role findRole = Role.ADMIN; + + // when + Optional found = applicantRepository.findByEmailAndRole(findEmail, findRole); + + // then + assertThat(found).isEmpty(); + } + + @Test + void 이메일과_역할목록으로_지원자를_조회시_목록에_포함된_역할이면_지원을_반환한다() { + // given + String email = "backoffice@example.com"; + Applicant applicant = createApplicant("백오피스", "01087654321", email, JobFamily.FE, Role.OPERATIONS); + applicantRepository.save(applicant); + + // when + Optional found = applicantRepository.findByEmailAndRoleIn( + email, + List.of(Role.ADMIN, Role.OPERATIONS) + ); + + // then + assertThat(found).isPresent(); + assertThat(found.get().getEmail()).isEqualTo(email); + assertThat(found.get().getRole()).isEqualTo(Role.OPERATIONS); + } + + @Test + void 이메일과_역할목록으로_지원자를_조회시_목록에_없는_역할이면_빈_Optional을_반환한다() { + // given + String email = "semester@example.com"; + Applicant applicant = createApplicant("지원자", "01099998888", email, JobFamily.BE, Role.SEMESTER); + applicantRepository.save(applicant); + + // when + Optional found = applicantRepository.findByEmailAndRoleIn( + email, + List.of(Role.ADMIN, Role.OPERATIONS, Role.SUPPORTER) + ); + + // then + assertThat(found).isEmpty(); + } + + @Test + void id와_역할목록으로_지원자를_조회시_목록에_포함된_역할이면_지원자를_반환한다() { + // given + Applicant applicant = createApplicant("운영진", "01011112222", "operations@example.com", JobFamily.PM, Role.OPERATIONS); + Applicant savedApplicant = applicantRepository.save(applicant); + + // when + Optional found = applicantRepository.findByIdAndRoleIn( + savedApplicant.getId(), + List.of(Role.ADMIN, Role.OPERATIONS, Role.SUPPORTER) + ); + + // then + assertThat(found).isPresent(); + assertThat(found.get().getId()).isEqualTo(savedApplicant.getId()); + assertThat(found.get().getRole()).isEqualTo(Role.OPERATIONS); + } + + @Test + void id와_역할목록으로_지원자를_조회시_목록에_없는_역할이면_빈_Optional을_반환한다() { + // given + Applicant applicant = createApplicant("지원자", "01033334444", "member@example.com", JobFamily.BE, Role.SEMESTER); + Applicant savedApplicant = applicantRepository.save(applicant); + + // when + Optional found = applicantRepository.findByIdAndRoleIn( + savedApplicant.getId(), + List.of(Role.ADMIN, Role.OPERATIONS, Role.SUPPORTER) + ); + + // then + assertThat(found).isEmpty(); + } + + private Applicant createApplicant(String name, String phoneNumber, String email, JobFamily jobFamily, Role role) { + return Applicant.builder() + .name(name) + .phoneNumber(phoneNumber) + .email(email) + .semesterId(1L) + .jobFamily(jobFamily) + .role(role) + .pin("123456") + .status(MemberStatus.ACTIVE) + .build(); + } +} diff --git a/src/test/java/org/ject/support/domain/applicant/service/ApplicantServiceTest.java b/src/test/java/org/ject/support/domain/applicant/service/ApplicantServiceTest.java new file mode 100644 index 00000000..daaa6ab5 --- /dev/null +++ b/src/test/java/org/ject/support/domain/applicant/service/ApplicantServiceTest.java @@ -0,0 +1,247 @@ +package org.ject.support.domain.applicant.service; + +import org.ject.support.base.UnitTestSupport; +import org.ject.support.common.security.jwt.JwtTokenProvider; +import org.ject.support.domain.applicant.dto.ApplicantDto.InitialProfileRequest; +import org.ject.support.domain.applicant.dto.ApplicantDto.RegisterRequest; +import org.ject.support.domain.applicant.dto.ApplicantDto.UpdatePinRequest; +import org.ject.support.domain.applicant.dto.ApplicantProfileResponse; +import org.ject.support.domain.applicant.entity.Applicant; +import org.ject.support.domain.applicant.exception.ApplicantErrorCode; +import org.ject.support.domain.applicant.exception.ApplicantException; +import org.ject.support.domain.applicant.repository.ApplicantRepository; +import org.ject.support.domain.member.CareerDetails; +import org.ject.support.domain.member.ExperiencePeriod; +import org.ject.support.domain.member.MemberStatus; +import org.ject.support.domain.member.Region; +import org.ject.support.domain.recruit.domain.Semester; +import org.ject.support.domain.recruit.repository.SemesterRepository; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.springframework.security.core.Authentication; +import org.springframework.security.crypto.password.PasswordEncoder; + +import java.util.List; +import java.util.Optional; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.BDDMockito.given; +import static org.mockito.Mockito.verify; + +class ApplicantServiceTest extends UnitTestSupport { + + @InjectMocks + private ApplicantService applicantService; + + @Mock + private ApplicantRepository applicantRepository; + + @Mock + private JwtTokenProvider jwtTokenProvider; + + @Mock + private PasswordEncoder passwordEncoder; + + @Mock + private Authentication authentication; + + @Mock + private SemesterRepository semesterRepository; + + private final String TEST_NAME = "홍길동"; + private final String TEST_EMAIL = "test@example.com"; + private final String TEST_PHONE_NUMBER = "01012345678"; + private final String TEST_PIN = "123456"; + private final String TEST_ENCODED_PIN = "encoded_pin"; + + @BeforeEach + void setUp() { + // 기본 설정 + } + + @Test + void 임시_지원자_등록_성공() { + // given + RegisterRequest request = new RegisterRequest(TEST_PIN); + Applicant applicant = Applicant.builder() + .id(1L) + .email(TEST_EMAIL) + .pin(TEST_ENCODED_PIN) + .status(MemberStatus.ACTIVE) + .build(); + Semester semester = Semester.builder() + .id(1L) + .name("1기") + .isRecruiting(true) + .build(); + + given(applicantRepository.findByEmail(TEST_EMAIL)).willReturn(Optional.empty()); + given(passwordEncoder.encode(TEST_PIN)).willReturn(TEST_ENCODED_PIN); + given(applicantRepository.save(any(Applicant.class))).willReturn(applicant); + given(jwtTokenProvider.createAuthenticationByApplicant(any(Applicant.class))).willReturn(authentication); + given(semesterRepository.findRecruitingSemester()).willReturn(Optional.of(semester)); + + // when + Authentication result = applicantService.registerTempApplicant(request, TEST_EMAIL); + + // then + assertThat(result).isEqualTo(authentication); + verify(applicantRepository).save(any(Applicant.class)); + verify(passwordEncoder).encode(TEST_PIN); + verify(jwtTokenProvider).createAuthenticationByApplicant(any(Applicant.class)); + } + + @Test + void 이미_존재하는_지원자인_경우_예외_발생() { + // given + RegisterRequest request = new RegisterRequest(TEST_PIN); + Applicant existingApplicant = Applicant.builder() + .email(TEST_EMAIL) + .pin(TEST_ENCODED_PIN) + .status(MemberStatus.ACTIVE) + .build(); + + given(applicantRepository.findByEmail(TEST_EMAIL)).willReturn(Optional.of(existingApplicant)); + + // when & then + assertThatThrownBy(() -> applicantService.registerTempApplicant(request, TEST_EMAIL)) + .isInstanceOf(ApplicantException.class) + .extracting(e -> ((ApplicantException) e).getErrorCode()) + .isEqualTo(ApplicantErrorCode.ALREADY_EXIST_APPLICANT); + } + + @Test + void 지원자_정보_업데이트_성공() { + // given + Long applicantId = 1L; + InitialProfileRequest request = new InitialProfileRequest(TEST_NAME, TEST_PHONE_NUMBER); + Applicant applicant = Applicant.builder() + .id(applicantId) + .email(TEST_EMAIL) + .pin(TEST_ENCODED_PIN) + .status(MemberStatus.ACTIVE) + .build(); + + given(applicantRepository.findById(applicantId)).willReturn(Optional.of(applicant)); + + // when + applicantService.registerInitialProfile(request, applicantId); + + // then + assertThat(applicant.getName()).isEqualTo(TEST_NAME); + assertThat(applicant.getPhoneNumber()).isEqualTo(TEST_PHONE_NUMBER); + verify(applicantRepository).findById(applicantId); + } + + @Test + void 존재하지_않는_지원자_정보_업데이트_시_예외_발생() { + // given + Long applicantId = 1L; + InitialProfileRequest request = new InitialProfileRequest(TEST_NAME, TEST_PHONE_NUMBER); + + given(applicantRepository.findById(applicantId)).willReturn(Optional.empty()); + + // when & then + assertThatThrownBy(() -> applicantService.registerInitialProfile(request, applicantId)) + .isInstanceOf(ApplicantException.class) + .extracting(e -> ((ApplicantException) e).getErrorCode()) + .isEqualTo(ApplicantErrorCode.NOT_FOUND_APPLICANT); + } + + @Test + void 핀번호_재설정_성공() { + // given + Long applicantId = 1L; + String newPin = "654321"; + UpdatePinRequest request = new UpdatePinRequest(newPin); + + Applicant applicant = Applicant.builder() + .id(applicantId) + .email(TEST_EMAIL) + .pin(TEST_ENCODED_PIN) + .status(MemberStatus.ACTIVE) + .build(); + + String newEncodedPin = "new_encoded_pin"; + + given(applicantRepository.findById(applicantId)).willReturn(Optional.of(applicant)); + given(passwordEncoder.encode(newPin)).willReturn(newEncodedPin); + + // when + applicantService.updatePin(request, applicantId); + + // then + assertThat(applicant.getPin()).isEqualTo(newEncodedPin); + verify(applicantRepository).findById(applicantId); + verify(passwordEncoder).encode(newPin); + } + + @Test + void 핀번호_재설정_실패_존재하지_않는_지원자() { + // given + Long applicantId = 1L; + UpdatePinRequest request = new UpdatePinRequest("654321"); + + given(applicantRepository.findById(applicantId)).willReturn(Optional.empty()); + + // when & then + assertThatThrownBy(() -> applicantService.updatePin(request, applicantId)) + .isInstanceOf(ApplicantException.class) + .extracting(e -> ((ApplicantException) e).getErrorCode()) + .isEqualTo(ApplicantErrorCode.NOT_FOUND_APPLICANT); + } + + @Test + void 존재하지않는_지원자ID로_지원자_프로필_정보를_조회할_경우_NOT_FOUND_APPLICANT_예외_발생() { + // given + Long applicantId = 1L; + given(applicantRepository.findById(applicantId)) + .willReturn(Optional.empty()); + + // when & then + assertThatThrownBy(() -> applicantService.getApplicantProfile(applicantId)) + .isInstanceOf(ApplicantException.class) + .extracting(e -> ((ApplicantException) e).getErrorCode()) + .isEqualTo(ApplicantErrorCode.NOT_FOUND_APPLICANT); + } + + @Test + void 지원자ID로_지원자_프로필_정보를_조회할_경우_지원자정보를_반환한다() { + // given + Long applicantId = 1L; + String name = "프로필"; + String phoneNumber = "01012345678"; + CareerDetails careerDetails = CareerDetails.EMPLOYEE; + Region region = Region.BUSAN; + ExperiencePeriod experiencePeriod = ExperiencePeriod.FIVE_PLUS; + List interestedDomains = List.of("BACKEND", "FRONTEND"); + + Applicant applicant = Applicant.builder() + .id(applicantId) + .name(name) + .phoneNumber(phoneNumber) + .careerDetails(careerDetails) + .region(region) + .experiencePeriod(experiencePeriod) + .interestedDomains(interestedDomains) + .build(); + + given(applicantRepository.findById(applicantId)) + .willReturn(Optional.of(applicant)); + + // when + ApplicantProfileResponse result = applicantService.getApplicantProfile(applicantId); + + // then + assertThat(result.id()).isEqualTo(applicantId); + assertThat(result.name()).isEqualTo(name); + assertThat(result.careerDetails()).isEqualTo(careerDetails); + assertThat(result.region()).isEqualTo(region); + assertThat(result.experiencePeriod()).isEqualTo(experiencePeriod); + assertThat(result.interestedDomains()).isEqualTo(interestedDomains); + } +} From 62c0e4b477a21673b2e2a65af6eceb667cc0aa1d Mon Sep 17 00:00:00 2001 From: Kang Tae Seong <69503955+kangkings@users.noreply.github.com> Date: Fri, 5 Jun 2026 12:54:06 +0900 Subject: [PATCH 2/4] =?UTF-8?q?refactor:=20=EC=9D=B8=EC=A6=9D=20=ED=9D=90?= =?UTF-8?q?=EB=A6=84=EC=9D=84=20Member=20->=20Applicant=20=EA=B8=B0?= =?UTF-8?q?=EC=A4=80=EC=9C=BC=EB=A1=9C=20=EC=A0=84=ED=99=98=20(#572)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * [refactor] 인증 대상 테이블을 applicant로 전환 * [Refactor] JWT claim 이름을 applicantId로 변경 * [Refactor] 일반 인증 흐름의 Member 의존을 Applicant로 변경 * [Refactor] 관리자 인증 흐름의 Member 의존을 Applicant로 변경 * [refactor] Member에서 이관 완료된 인증 엔드포인트, 로직 제거 * [chore] 함수 제거 및 주석 추가 --- .../dto/AdminAccountCreateRequest.java | 6 +- .../account/dto/AdminAccountResponse.java | 4 +- .../account/service/AdminAccountService.java | 30 +- .../admin/auth/service/AdminAuthService.java | 14 +- .../admin/component/AdminMemberComponent.java | 27 +- ... => AuthenticatedApplicantIdResolver.java} | 4 +- .../common/security/CustomSuccessHandler.java | 10 +- .../security/CustomUserDetailService.java | 16 +- .../common/security/CustomUserDetails.java | 22 +- .../common/security/config/WebConfig.java | 4 +- .../security/jwt/JwtAuthenticationFilter.java | 8 +- .../common/security/jwt/JwtTokenProvider.java | 30 +- .../auth/controller/AuthController.java | 4 +- .../auth/controller/DevAuthController.java | 12 +- .../domain/auth/service/AuthService.java | 32 +- .../member/controller/MemberApiSpec.java | 43 --- .../member/controller/MemberController.java | 97 ------ .../support/domain/member/dto/MemberDto.java | 42 --- .../member/dto/MemberProfileResponse.java | 32 -- .../domain/member/service/MemberService.java | 106 ------- .../AdminAccountControllerTest.java | 4 +- .../service/AdminAccountServiceTest.java | 128 ++++---- .../auth/service/AdminAuthServiceTest.java | 34 +-- .../component/AdminMemberComponentTest.java | 56 ++-- ...AuthenticatedApplicantIdResolverTest.java} | 18 +- .../security/CustomUserDetailServiceTest.java | 26 +- .../security/CustomUserDetailsTest.java | 17 +- .../jwt/JwtAuthenticationFilterTest.java | 30 +- .../security/jwt/JwtTokenProviderTest.java | 42 +-- .../apply/controller/ApplyControllerTest.java | 4 +- .../domain/auth/AuthControllerTest.java | 6 +- .../support/domain/auth/AuthServiceTest.java | 64 ++-- .../controller/MemberControllerTest.java | 288 ------------------ .../member/service/MemberServiceTest.java | 247 --------------- .../support/testconfig/AuthenticatedUser.java | 1 + ...thenticatedUserSecurityContextFactory.java | 4 +- 36 files changed, 329 insertions(+), 1183 deletions(-) rename src/main/java/org/ject/support/common/security/{AuthenticatedMemberIdResolver.java => AuthenticatedApplicantIdResolver.java} (91%) delete mode 100644 src/main/java/org/ject/support/domain/member/controller/MemberApiSpec.java delete mode 100644 src/main/java/org/ject/support/domain/member/controller/MemberController.java delete mode 100644 src/main/java/org/ject/support/domain/member/dto/MemberDto.java delete mode 100644 src/main/java/org/ject/support/domain/member/dto/MemberProfileResponse.java delete mode 100644 src/main/java/org/ject/support/domain/member/service/MemberService.java rename src/test/java/org/ject/support/common/security/{AuthenticatedMemberIdResolverTest.java => AuthenticatedApplicantIdResolverTest.java} (90%) delete mode 100644 src/test/java/org/ject/support/domain/member/controller/MemberControllerTest.java delete mode 100644 src/test/java/org/ject/support/domain/member/service/MemberServiceTest.java diff --git a/src/main/java/org/ject/support/admin/account/dto/AdminAccountCreateRequest.java b/src/main/java/org/ject/support/admin/account/dto/AdminAccountCreateRequest.java index 240ff9fc..c5c5bbcc 100644 --- a/src/main/java/org/ject/support/admin/account/dto/AdminAccountCreateRequest.java +++ b/src/main/java/org/ject/support/admin/account/dto/AdminAccountCreateRequest.java @@ -5,9 +5,9 @@ import jakarta.validation.constraints.NotBlank; import jakarta.validation.constraints.NotNull; import jakarta.validation.constraints.Size; +import org.ject.support.domain.applicant.entity.Applicant; import org.ject.support.domain.member.MemberType; import org.ject.support.domain.member.Role; -import org.ject.support.domain.member.entity.Member; @Schema(description = "관리자 계정 생성 요청") public record AdminAccountCreateRequest( @@ -36,8 +36,8 @@ public record AdminAccountCreateRequest( private static final long DEFAULT_ADMIN_ACCOUNT_SEMESTER_ID = 1L; - public Member toEntity(final String encodedPassword) { - return Member.builder() + public Applicant toEntity(final String encodedPassword) { + return Applicant.builder() .email(email) .pin(encodedPassword) .name(normalizeName()) diff --git a/src/main/java/org/ject/support/admin/account/dto/AdminAccountResponse.java b/src/main/java/org/ject/support/admin/account/dto/AdminAccountResponse.java index 85135f9c..d9e90b0e 100644 --- a/src/main/java/org/ject/support/admin/account/dto/AdminAccountResponse.java +++ b/src/main/java/org/ject/support/admin/account/dto/AdminAccountResponse.java @@ -1,8 +1,8 @@ package org.ject.support.admin.account.dto; +import org.ject.support.domain.applicant.dto.ApplicantAccountProjection; import org.ject.support.domain.member.MemberStatus; import org.ject.support.domain.member.Role; -import org.ject.support.domain.member.dto.MemberAccountProjection; public record AdminAccountResponse( Long id, @@ -12,7 +12,7 @@ public record AdminAccountResponse( MemberStatus status ) { - public static AdminAccountResponse from(final MemberAccountProjection projection) { + public static AdminAccountResponse from(final ApplicantAccountProjection projection) { return new AdminAccountResponse( projection.id(), projection.email(), diff --git a/src/main/java/org/ject/support/admin/account/service/AdminAccountService.java b/src/main/java/org/ject/support/admin/account/service/AdminAccountService.java index c6dec3c0..733816ef 100644 --- a/src/main/java/org/ject/support/admin/account/service/AdminAccountService.java +++ b/src/main/java/org/ject/support/admin/account/service/AdminAccountService.java @@ -10,11 +10,11 @@ import org.ject.support.admin.component.AdminMemberComponent; import org.ject.support.admin.exception.AdminErrorCode; import org.ject.support.admin.exception.AdminException; +import org.ject.support.domain.applicant.entity.Applicant; +import org.ject.support.domain.applicant.entity.ApplicantEditor; +import org.ject.support.domain.applicant.repository.ApplicantRepository; import org.ject.support.domain.member.MemberStatus; import org.ject.support.domain.member.Role; -import org.ject.support.domain.member.entity.Member; -import org.ject.support.domain.member.entity.MemberEditor; -import org.ject.support.domain.member.repository.MemberRepository; import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; import org.springframework.security.crypto.password.PasswordEncoder; @@ -28,14 +28,14 @@ @Transactional(readOnly = true) public class AdminAccountService { - private final MemberRepository memberRepository; + private final ApplicantRepository applicantRepository; private final AdminMemberComponent adminMemberComponent; private final PasswordEncoder passwordEncoder; public Page findAccounts(final AdminAccountSearchCondition condition, final Pageable pageable) { validateBackofficeRoles(condition.roles()); - return memberRepository.findAccounts(condition, pageable) + return applicantRepository.findAccounts(condition, pageable) .map(AdminAccountResponse::from); } @@ -45,7 +45,7 @@ public void createAccount(final AdminAccountCreateRequest request) { validateEmailUniqueness(request); final String encodedPassword = passwordEncoder.encode(request.password()); - memberRepository.save(request.toEntity(encodedPassword)); + applicantRepository.save(request.toEntity(encodedPassword)); } @Transactional @@ -56,29 +56,29 @@ public void updateAccount(final Long requesterId, validateNotLockingSelf(requesterId, memberId, request.active()); validateNotChangingOwnAdminRole(requesterId, memberId, request.role()); - final Member member = adminMemberComponent.getRequiredBackofficeMemberById(memberId); + final Applicant applicant = adminMemberComponent.getRequiredBackofficeMemberById(memberId); final MemberStatus status = request.active() ? MemberStatus.ACTIVE : MemberStatus.LOCKED; - final MemberEditor editor = member.toEditor() + final ApplicantEditor editor = applicant.toEditor() .name(request.normalizeName()) .role(request.role()) .status(status) .build(); - member.edit(editor); + applicant.edit(editor); } @Transactional public void updateRole(final Long memberId, final AdminAccountRoleUpdateRequest request) { validateBackofficeRole(request.role()); - final Member member = adminMemberComponent.getRequiredBackofficeMemberById(memberId); + final Applicant applicant = adminMemberComponent.getRequiredBackofficeMemberById(memberId); - final MemberEditor editor = member.toEditor() + final ApplicantEditor editor = applicant.toEditor() .role(request.role()) .build(); - member.edit(editor); + applicant.edit(editor); } @Transactional @@ -87,10 +87,10 @@ public void updateActive(final Long requesterId, final AdminAccountActiveUpdateRequest request) { validateNotLockingSelf(requesterId, memberId, request.active()); - final Member member = adminMemberComponent.getRequiredBackofficeMemberById(memberId); + final Applicant applicant = adminMemberComponent.getRequiredBackofficeMemberById(memberId); final MemberStatus status = request.active() ? MemberStatus.ACTIVE : MemberStatus.LOCKED; - adminMemberComponent.changeMemberStatus(member, status); + adminMemberComponent.changeMemberStatus(applicant, status); } private void validateNotLockingSelf(final Long requesterId, @@ -126,7 +126,7 @@ private void validateBackofficeRoles(final List roles) { } private void validateEmailUniqueness(final AdminAccountCreateRequest request) { - if (memberRepository.existsByEmail(request.email())) { + if (applicantRepository.existsByEmail(request.email())) { throw new AdminException(AdminErrorCode.DUPLICATE_ADMIN_EMAIL); } } diff --git a/src/main/java/org/ject/support/admin/auth/service/AdminAuthService.java b/src/main/java/org/ject/support/admin/auth/service/AdminAuthService.java index 18a352b5..2caf37a2 100644 --- a/src/main/java/org/ject/support/admin/auth/service/AdminAuthService.java +++ b/src/main/java/org/ject/support/admin/auth/service/AdminAuthService.java @@ -6,8 +6,8 @@ import org.ject.support.admin.component.AdminMemberComponent; import org.ject.support.admin.exception.AdminErrorCode; import org.ject.support.admin.exception.AdminException; +import org.ject.support.domain.applicant.entity.Applicant; import org.ject.support.domain.member.MemberStatus; -import org.ject.support.domain.member.entity.Member; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.security.core.Authentication; import org.springframework.security.crypto.password.PasswordEncoder; @@ -36,17 +36,17 @@ public class AdminAuthService { public Authentication authenticateAdmin(String email, String password) { validateLoginAttemptLimit(email); - Member member = adminMemberComponent.findBackofficeMemberByEmail(email) + Applicant applicant = adminMemberComponent.findBackofficeMemberByEmail(email) .orElseThrow(() -> handleInvalidAdminCredentials(email)); - if (!passwordEncoder.matches(password, member.getPin())) { + if (!passwordEncoder.matches(password, applicant.getPin())) { throw handleInvalidAdminCredentials(email); } - checkMemberStatus(member); + checkMemberStatus(applicant); clearLoginFailState(email); - return jwtTokenProvider.createAuthenticationByMember(member); + return jwtTokenProvider.createAuthenticationByApplicant(applicant); } private AdminException handleInvalidAdminCredentials(String email) { @@ -79,8 +79,8 @@ private void clearLoginFailState(String email) { redisTemplate.delete(ADMIN_LOGIN_PASSWORD_BLOCK_KEY_PREFIX + email); } - private void checkMemberStatus(Member member) { - if (member.getStatus() == MemberStatus.LOCKED) { + private void checkMemberStatus(Applicant applicant) { + if (applicant.getStatus() == MemberStatus.LOCKED) { throw new AdminException(AdminErrorCode.LOCKED_ADMIN); } } diff --git a/src/main/java/org/ject/support/admin/component/AdminMemberComponent.java b/src/main/java/org/ject/support/admin/component/AdminMemberComponent.java index 6aab42c3..a92a6579 100644 --- a/src/main/java/org/ject/support/admin/component/AdminMemberComponent.java +++ b/src/main/java/org/ject/support/admin/component/AdminMemberComponent.java @@ -3,10 +3,10 @@ import lombok.RequiredArgsConstructor; import org.ject.support.admin.exception.AdminErrorCode; import org.ject.support.admin.exception.AdminException; +import org.ject.support.domain.applicant.entity.Applicant; +import org.ject.support.domain.applicant.repository.ApplicantRepository; import org.ject.support.domain.member.MemberStatus; import org.ject.support.domain.member.Role; -import org.ject.support.domain.member.entity.Member; -import org.ject.support.domain.member.repository.MemberRepository; import org.springframework.stereotype.Component; import org.springframework.transaction.annotation.Propagation; import org.springframework.transaction.annotation.Transactional; @@ -18,30 +18,31 @@ @Transactional(readOnly = true) public class AdminMemberComponent { - private final MemberRepository memberRepository; + private final ApplicantRepository applicantRepository; - public Member getRequiredBackofficeMemberByEmail(String email) { - return memberRepository.findByEmailAndRoleIn(email, Role.backofficeRoles()) + // TODO: 내부 주체는 Applicant지만 관리자 계정 관리 맥락상 Member 네이밍을 유지한다. + public Applicant getRequiredBackofficeMemberByEmail(String email) { + return applicantRepository.findByEmailAndRoleIn(email, Role.backofficeRoles()) .orElseThrow(() -> new AdminException(AdminErrorCode.NOT_FOUND_ADMIN)); } - public Member getRequiredBackofficeMemberById(Long id) { - return memberRepository.findByIdAndRoleIn(id, Role.backofficeRoles()) + public Applicant getRequiredBackofficeMemberById(Long id) { + return applicantRepository.findByIdAndRoleIn(id, Role.backofficeRoles()) .orElseThrow(() -> new AdminException(AdminErrorCode.NOT_FOUND_ADMIN)); } - public Optional findBackofficeMemberByEmail(String email) { - return memberRepository.findByEmailAndRoleIn(email, Role.backofficeRoles()); + public Optional findBackofficeMemberByEmail(String email) { + return applicantRepository.findByEmailAndRoleIn(email, Role.backofficeRoles()); } @Transactional(propagation = Propagation.REQUIRES_NEW) - public void changeMemberStatus(Member member, MemberStatus status) { - if (member.getStatus() == status) { + public void changeMemberStatus(Applicant applicant, MemberStatus status) { + if (applicant.getStatus() == status) { return; } - member.edit(member.toEditor() + applicant.edit(applicant.toEditor() .status(status) .build()); - memberRepository.save(member); + applicantRepository.save(applicant); } } diff --git a/src/main/java/org/ject/support/common/security/AuthenticatedMemberIdResolver.java b/src/main/java/org/ject/support/common/security/AuthenticatedApplicantIdResolver.java similarity index 91% rename from src/main/java/org/ject/support/common/security/AuthenticatedMemberIdResolver.java rename to src/main/java/org/ject/support/common/security/AuthenticatedApplicantIdResolver.java index c3a1fd82..bd9df5f3 100644 --- a/src/main/java/org/ject/support/common/security/AuthenticatedMemberIdResolver.java +++ b/src/main/java/org/ject/support/common/security/AuthenticatedApplicantIdResolver.java @@ -16,7 +16,7 @@ @Component @RequiredArgsConstructor -public class AuthenticatedMemberIdResolver implements HandlerMethodArgumentResolver { +public class AuthenticatedApplicantIdResolver implements HandlerMethodArgumentResolver { @Override public boolean supportsParameter(MethodParameter parameter) { @@ -33,6 +33,6 @@ public Long resolveArgument(MethodParameter parameter, ModelAndViewContainer mav } CustomUserDetails userDetails = (CustomUserDetails) authentication.getPrincipal(); - return userDetails.getMemberId(); + return userDetails.getApplicantId(); } } diff --git a/src/main/java/org/ject/support/common/security/CustomSuccessHandler.java b/src/main/java/org/ject/support/common/security/CustomSuccessHandler.java index be4e2fe6..752a90d6 100644 --- a/src/main/java/org/ject/support/common/security/CustomSuccessHandler.java +++ b/src/main/java/org/ject/support/common/security/CustomSuccessHandler.java @@ -22,8 +22,8 @@ public class CustomSuccessHandler extends SimpleUrlAuthenticationSuccessHandler public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) { CustomUserDetails customUserDetails = (CustomUserDetails) authentication.getPrincipal(); - String accessToken = jwtTokenProvider.createAccessToken(authentication, customUserDetails.getMemberId()); - String refreshToken = jwtTokenProvider.createRefreshToken(authentication, customUserDetails.getMemberId()); + String accessToken = jwtTokenProvider.createAccessToken(authentication, customUserDetails.getApplicantId()); + String refreshToken = jwtTokenProvider.createRefreshToken(authentication, customUserDetails.getApplicantId()); response.addHeader(HttpHeaders.SET_COOKIE, jwtCookieProvider.createRefreshCookie(refreshToken).toString()); response.addHeader(HttpHeaders.SET_COOKIE, jwtCookieProvider.createAccessCookie(accessToken).toString()); @@ -44,10 +44,10 @@ public void onAuthenticationSuccess(HttpServletResponse response, String email) * 리프레시 토큰을 통한 액세스 토큰 재발급 성공 시 호출되는 메서드 * @param response HttpServletResponse * @param refreshToken 재발급된 리프레시 토큰 - * @param memberId 회원 ID + * @param applicantId Applicant ID */ - public void onAuthenticationSuccess(HttpServletResponse response, String refreshToken, Long memberId) { - String newAccessToken = jwtTokenProvider.reissueAccessToken(refreshToken, memberId); + public void onAuthenticationSuccess(HttpServletResponse response, String refreshToken, Long applicantId) { + String newAccessToken = jwtTokenProvider.reissueAccessToken(refreshToken, applicantId); response.addHeader(HttpHeaders.SET_COOKIE, jwtCookieProvider.createAccessCookie(newAccessToken).toString()); } diff --git a/src/main/java/org/ject/support/common/security/CustomUserDetailService.java b/src/main/java/org/ject/support/common/security/CustomUserDetailService.java index 7bdd86a3..9439df40 100644 --- a/src/main/java/org/ject/support/common/security/CustomUserDetailService.java +++ b/src/main/java/org/ject/support/common/security/CustomUserDetailService.java @@ -2,25 +2,25 @@ import lombok.RequiredArgsConstructor; -import org.ject.support.domain.member.entity.Member; -import org.ject.support.domain.member.exception.MemberException; -import org.ject.support.domain.member.repository.MemberRepository; +import org.ject.support.domain.applicant.entity.Applicant; +import org.ject.support.domain.applicant.exception.ApplicantException; +import org.ject.support.domain.applicant.repository.ApplicantRepository; import org.springframework.security.core.userdetails.UserDetailsService; import org.springframework.security.core.userdetails.UsernameNotFoundException; import org.springframework.stereotype.Service; -import static org.ject.support.domain.member.exception.MemberErrorCode.NOT_FOUND_MEMBER; +import static org.ject.support.domain.applicant.exception.ApplicantErrorCode.NOT_FOUND_APPLICANT; @Service @RequiredArgsConstructor public class CustomUserDetailService implements UserDetailsService { - private final MemberRepository memberRepository; + private final ApplicantRepository applicantRepository; @Override public CustomUserDetails loadUserByUsername(String username) throws UsernameNotFoundException { - Member member = memberRepository.findByEmail(username) - .orElseThrow(() -> new MemberException(NOT_FOUND_MEMBER)); - return new CustomUserDetails(member); + Applicant applicant = applicantRepository.findByEmail(username) + .orElseThrow(() -> new ApplicantException(NOT_FOUND_APPLICANT)); + return new CustomUserDetails(applicant); } } diff --git a/src/main/java/org/ject/support/common/security/CustomUserDetails.java b/src/main/java/org/ject/support/common/security/CustomUserDetails.java index 85dbf31a..16e9a0e5 100644 --- a/src/main/java/org/ject/support/common/security/CustomUserDetails.java +++ b/src/main/java/org/ject/support/common/security/CustomUserDetails.java @@ -2,33 +2,35 @@ import java.util.ArrayList; import java.util.Collection; -import lombok.Getter; +import org.ject.support.domain.applicant.entity.Applicant; import org.ject.support.domain.member.Permission; import org.ject.support.domain.member.Role; import org.ject.support.domain.member.RolePermissions; -import org.ject.support.domain.member.entity.Member; import org.springframework.security.core.GrantedAuthority; import org.springframework.security.core.userdetails.UserDetails; public class CustomUserDetails implements UserDetails { - @Getter - private final Long memberId; + private final Long applicantId; private final String email; private final Role role; - public CustomUserDetails(Member member) { - this.memberId = member.getId(); - this.email = member.getEmail(); - this.role = member.getRole(); + public CustomUserDetails(Applicant applicant) { + this.applicantId = applicant.getId(); + this.email = applicant.getEmail(); + this.role = applicant.getRole(); } - public CustomUserDetails(String email, Long memberId, Role role) { + public CustomUserDetails(String email, Long applicantId, Role role) { this.email = email; - this.memberId = memberId; + this.applicantId = applicantId; this.role = role; } + public Long getApplicantId() { + return applicantId; + } + @Override public Collection getAuthorities() { Collection collection = new ArrayList<>(); diff --git a/src/main/java/org/ject/support/common/security/config/WebConfig.java b/src/main/java/org/ject/support/common/security/config/WebConfig.java index 9e997994..3ab690c8 100644 --- a/src/main/java/org/ject/support/common/security/config/WebConfig.java +++ b/src/main/java/org/ject/support/common/security/config/WebConfig.java @@ -2,7 +2,7 @@ import java.util.List; import lombok.RequiredArgsConstructor; -import org.ject.support.common.security.AuthenticatedMemberIdResolver; +import org.ject.support.common.security.AuthenticatedApplicantIdResolver; import org.springframework.context.annotation.Configuration; import org.springframework.web.method.support.HandlerMethodArgumentResolver; import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; @@ -13,6 +13,6 @@ public class WebConfig implements WebMvcConfigurer { @Override public void addArgumentResolvers(List resolvers) { - resolvers.add(new AuthenticatedMemberIdResolver()); + resolvers.add(new AuthenticatedApplicantIdResolver()); } } diff --git a/src/main/java/org/ject/support/common/security/jwt/JwtAuthenticationFilter.java b/src/main/java/org/ject/support/common/security/jwt/JwtAuthenticationFilter.java index da4fef3d..f2585dfa 100644 --- a/src/main/java/org/ject/support/common/security/jwt/JwtAuthenticationFilter.java +++ b/src/main/java/org/ject/support/common/security/jwt/JwtAuthenticationFilter.java @@ -12,9 +12,9 @@ import org.ject.support.common.exception.GlobalErrorCode; import org.ject.support.common.exception.GlobalException; import org.ject.support.common.security.CustomUserDetails; +import org.ject.support.domain.applicant.repository.ApplicantRepository; import org.ject.support.domain.member.MemberStatus; import org.ject.support.domain.member.Role; -import org.ject.support.domain.member.repository.MemberRepository; import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; import org.springframework.security.core.Authentication; import org.springframework.security.core.GrantedAuthority; @@ -46,7 +46,7 @@ public class JwtAuthenticationFilter extends OncePerRequestFilter { .collect(Collectors.toUnmodifiableSet()); private final JwtTokenProvider jwtTokenProvider; - private final MemberRepository memberRepository; + private final ApplicantRepository applicantRepository; @Override protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) { @@ -107,10 +107,10 @@ private void validateBackofficeMemberStatus(Authentication authentication) { } CustomUserDetails userDetails = (CustomUserDetails) authentication.getPrincipal(); - var member = memberRepository.findByIdAndRoleIn(userDetails.getMemberId(), Role.backofficeRoles()) + var applicant = applicantRepository.findByIdAndRoleIn(userDetails.getApplicantId(), Role.backofficeRoles()) .orElseThrow(() -> new AdminException(AdminErrorCode.NOT_FOUND_ADMIN)); - if (member.getStatus() == MemberStatus.LOCKED) { + if (applicant.getStatus() == MemberStatus.LOCKED) { throw new AdminException(AdminErrorCode.LOCKED_ADMIN); } } diff --git a/src/main/java/org/ject/support/common/security/jwt/JwtTokenProvider.java b/src/main/java/org/ject/support/common/security/jwt/JwtTokenProvider.java index 30d04ef1..f99df3ff 100644 --- a/src/main/java/org/ject/support/common/security/jwt/JwtTokenProvider.java +++ b/src/main/java/org/ject/support/common/security/jwt/JwtTokenProvider.java @@ -17,7 +17,6 @@ import org.ject.support.common.security.CustomUserDetails; import org.ject.support.domain.applicant.entity.Applicant; import org.ject.support.domain.member.Role; -import org.ject.support.domain.member.entity.Member; import org.springframework.beans.factory.annotation.Value; import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; import org.springframework.security.core.Authentication; @@ -47,10 +46,10 @@ public void init() { this.secretKey = Keys.hmacShaKeyFor(decodeSecret); } - public String createAccessToken(Authentication authentication, Long memberId) { // TODO: 리팩토링 필요 + public String createAccessToken(Authentication authentication, Long applicantId) { // TODO: 리팩토링 필요 validateAuthentication(authentication); Claims claims = Jwts.claims(); - claims.put("memberId", memberId); + claims.put("applicantId", applicantId); String role = ((CustomUserDetails) authentication.getPrincipal()).getAuthorities().iterator().next().getAuthority(); claims.put("role", role); claims.setSubject(authentication.getName()); @@ -86,10 +85,10 @@ public String createToken(Claims claims, long expirationMillis) { /** * Refresh 토큰 생성 */ - public String createRefreshToken(Authentication authentication, Long memberId) { + public String createRefreshToken(Authentication authentication, Long applicantId) { validateAuthentication(authentication); Claims claims = Jwts.claims(); - claims.put("memberId", memberId); + claims.put("applicantId", applicantId); claims.setSubject(authentication.getName()); String role = ((CustomUserDetails) authentication.getPrincipal()).getAuthorities().iterator().next().getAuthority(); claims.put("role", role); @@ -108,7 +107,7 @@ public String createRefreshToken(Authentication authentication, Long memberId) { public Authentication getAuthenticationByToken(String token) { Claims claims = Jwts.parserBuilder().setSigningKey(secretKey).build().parseClaimsJws(token).getBody(); String email = claims.getSubject(); - Long memberId = claims.get("memberId", Long.class); + Long applicantId = claims.get("applicantId", Long.class); String roleStr = claims.get("role", String.class).toUpperCase(); // "ROLE_" 접두사가 있으면 제거 @@ -118,7 +117,7 @@ public Authentication getAuthenticationByToken(String token) { Role role = Role.valueOf(roleStr); - CustomUserDetails userDetails = new CustomUserDetails(email, memberId, role); + CustomUserDetails userDetails = new CustomUserDetails(email, applicantId, role); return new UsernamePasswordAuthenticationToken(userDetails, "", userDetails.getAuthorities()); } @@ -167,24 +166,19 @@ private void validateAuthentication(Authentication authentication) { } } - public String reissueAccessToken(String refreshToken, Long memberId) { + public String reissueAccessToken(String refreshToken, Long applicantId) { validateToken(refreshToken); Authentication authentication = getAuthenticationByToken(refreshToken); - return createAccessToken(authentication, memberId); + return createAccessToken(authentication, applicantId); } - public Long extractMemberId(String refreshToken) { - return getMemberId(refreshToken); + public Long extractApplicantId(String refreshToken) { + return getApplicantId(refreshToken); } - public Long getMemberId(String token) { + public Long getApplicantId(String token) { Claims claims = Jwts.parserBuilder().setSigningKey(secretKey).build().parseClaimsJws(token).getBody(); - return claims.get("memberId", Long.class); - } - - public Authentication createAuthenticationByMember(Member member) { - CustomUserDetails userDetails = new CustomUserDetails(member); - return new UsernamePasswordAuthenticationToken(userDetails, "", userDetails.getAuthorities()); + return claims.get("applicantId", Long.class); } public Authentication createAuthenticationByApplicant(Applicant applicant) { diff --git a/src/main/java/org/ject/support/domain/auth/controller/AuthController.java b/src/main/java/org/ject/support/domain/auth/controller/AuthController.java index b33c044e..6e00cf88 100644 --- a/src/main/java/org/ject/support/domain/auth/controller/AuthController.java +++ b/src/main/java/org/ject/support/domain/auth/controller/AuthController.java @@ -62,8 +62,8 @@ public boolean verifyAuthCode(@RequestBody VerifyAuthCodeRequest verifyAuthCodeR @PreAuthorize("permitAll()") public boolean refreshToken(HttpServletRequest request, HttpServletResponse response) { String refreshToken = jwtTokenProvider.resolveRefreshToken(request); - Long memberId = authService.refreshAccessToken(refreshToken); - customSuccessHandler.onAuthenticationSuccess(response, refreshToken, memberId); + Long applicantId = authService.refreshAccessToken(refreshToken); + customSuccessHandler.onAuthenticationSuccess(response, refreshToken, applicantId); return true; } diff --git a/src/main/java/org/ject/support/domain/auth/controller/DevAuthController.java b/src/main/java/org/ject/support/domain/auth/controller/DevAuthController.java index f0927250..2622e409 100644 --- a/src/main/java/org/ject/support/domain/auth/controller/DevAuthController.java +++ b/src/main/java/org/ject/support/domain/auth/controller/DevAuthController.java @@ -7,8 +7,8 @@ import lombok.AllArgsConstructor; import org.ject.support.common.security.CustomUserDetails; import org.ject.support.common.security.jwt.JwtTokenProvider; -import org.ject.support.domain.member.entity.Member; -import org.ject.support.domain.member.repository.MemberRepository; +import org.ject.support.domain.applicant.entity.Applicant; +import org.ject.support.domain.applicant.repository.ApplicantRepository; import org.springframework.context.annotation.Profile; import org.springframework.security.core.Authentication; import org.springframework.web.bind.annotation.PostMapping; @@ -20,7 +20,7 @@ @Profile({"local", "dev"}) public class DevAuthController { - private final MemberRepository memberRepository; + private final ApplicantRepository applicantRepository; private final JwtTokenProvider jwtTokenProvider; @Operation( @@ -29,11 +29,11 @@ public class DevAuthController { ) @PostMapping("/access-token") public String getToken(@Valid @RequestBody EmailRequest request) { - Member member = memberRepository.findByEmail(request.email) + Applicant applicant = applicantRepository.findByEmail(request.email) .orElseThrow(() -> new IllegalArgumentException("존재하지 않는 이메일입니다.")); - Authentication authentication = jwtTokenProvider.createAuthenticationByMember(member); + Authentication authentication = jwtTokenProvider.createAuthenticationByApplicant(applicant); CustomUserDetails customUserDetails = (CustomUserDetails) authentication.getPrincipal(); - return jwtTokenProvider.createAccessToken(authentication, customUserDetails.getMemberId()); + return jwtTokenProvider.createAccessToken(authentication, customUserDetails.getApplicantId()); } public record EmailRequest( diff --git a/src/main/java/org/ject/support/domain/auth/service/AuthService.java b/src/main/java/org/ject/support/domain/auth/service/AuthService.java index 2243a900..518a74ae 100644 --- a/src/main/java/org/ject/support/domain/auth/service/AuthService.java +++ b/src/main/java/org/ject/support/domain/auth/service/AuthService.java @@ -5,11 +5,11 @@ import io.jsonwebtoken.JwtException; import lombok.RequiredArgsConstructor; import org.ject.support.common.security.jwt.JwtTokenProvider; +import org.ject.support.domain.applicant.entity.Applicant; +import org.ject.support.domain.applicant.exception.ApplicantException; +import org.ject.support.domain.applicant.repository.ApplicantRepository; import org.ject.support.domain.auth.dto.AuthVerificationResult; import org.ject.support.domain.auth.exception.AuthException; -import org.ject.support.domain.member.entity.Member; -import org.ject.support.domain.member.exception.MemberException; -import org.ject.support.domain.member.repository.MemberRepository; import org.ject.support.external.email.domain.EmailTemplate; import org.ject.support.external.email.exception.EmailErrorCode; import org.ject.support.external.email.exception.EmailException; @@ -24,7 +24,7 @@ import static org.ject.support.domain.auth.exception.AuthErrorCode.INVALID_CREDENTIALS; import static org.ject.support.domain.auth.exception.AuthErrorCode.INVALID_REFRESH_TOKEN; import static org.ject.support.domain.auth.exception.AuthErrorCode.NOT_FOUND_AUTH_CODE; -import static org.ject.support.domain.member.exception.MemberErrorCode.NOT_FOUND_MEMBER; +import static org.ject.support.domain.applicant.exception.ApplicantErrorCode.NOT_FOUND_APPLICANT; @Service @RequiredArgsConstructor @@ -32,7 +32,7 @@ public class AuthService { private final RedisTemplate redisTemplate; private final JwtTokenProvider jwtTokenProvider; - private final MemberRepository memberRepository; + private final ApplicantRepository applicantRepository; private final PasswordEncoder passwordEncoder; /** @@ -69,9 +69,9 @@ public AuthVerificationResult verifyAuthCodeByTemplate(String email, String auth private Authentication verifyEmailByAuthCodeOnly(String email, String userInputCode) { // 인증번호 검증 verifyAuthCode(email, userInputCode); - Member member = findMember(email); + Applicant applicant = findApplicant(email); - Authentication authentication = jwtTokenProvider.createAuthenticationByMember(member); + Authentication authentication = jwtTokenProvider.createAuthenticationByApplicant(applicant); deleteAuthCode(email); return authentication; @@ -87,20 +87,21 @@ private Authentication verifyEmailByAuthCodeOnly(String email, String userInputC @Transactional public Authentication loginWithPin(String email, String pin) { // 이메일로 회원 조회 - Member member = findMember(email); + Applicant applicant = findApplicant(email); // PIN 번호 검증 - if (!passwordEncoder.matches(pin, member.getPin())) { + if (!passwordEncoder.matches(pin, applicant.getPin())) { throw new AuthException(INVALID_CREDENTIALS); } // 인증 및 토큰 발급 - return jwtTokenProvider.createAuthenticationByMember(member); + return jwtTokenProvider.createAuthenticationByApplicant(applicant); } - private Member findMember(String email) { - return memberRepository.findByEmail(email) - .orElseThrow(() -> new MemberException(NOT_FOUND_MEMBER)); + private Applicant findApplicant(String email) { + //Todo: Applicant는 이메일 유니크 제약없음(중복 발생) -> 고유하게 식별하도록 추가 개선 필요 + return applicantRepository.findByEmail(email) + .orElseThrow(() -> new ApplicantException(NOT_FOUND_APPLICANT)); } private void verifyAuthCode(String email, String userInputCode) { @@ -137,7 +138,7 @@ public Long refreshAccessToken(String refreshToken) { throw new AuthException(INVALID_REFRESH_TOKEN); } - return jwtTokenProvider.extractMemberId(refreshToken); + return jwtTokenProvider.extractApplicantId(refreshToken); } catch (ExpiredJwtException e) { throw new AuthException(EXPIRED_REFRESH_TOKEN); } catch (JwtException e) { @@ -146,6 +147,7 @@ public Long refreshAccessToken(String refreshToken) { } public boolean isExistMember(String email) { - return memberRepository.existsByEmail(email); + //Todo: Applicant는 이메일 유니크 제약없음(중복 발생) -> 고유하게 식별하도록 추가 개선 필요 + return applicantRepository.existsByEmail(email); } } diff --git a/src/main/java/org/ject/support/domain/member/controller/MemberApiSpec.java b/src/main/java/org/ject/support/domain/member/controller/MemberApiSpec.java deleted file mode 100644 index dec7a010..00000000 --- a/src/main/java/org/ject/support/domain/member/controller/MemberApiSpec.java +++ /dev/null @@ -1,43 +0,0 @@ -package org.ject.support.domain.member.controller; - -import io.swagger.v3.oas.annotations.Operation; -import io.swagger.v3.oas.annotations.tags.Tag; -import jakarta.servlet.http.HttpServletRequest; -import jakarta.servlet.http.HttpServletResponse; -import jakarta.validation.Valid; -import org.ject.support.common.security.AuthPrincipal; -import org.ject.support.domain.member.dto.MemberDto; -import org.ject.support.domain.member.dto.MemberProfileResponse; -import org.springframework.web.bind.annotation.RequestBody; - -@Tag(name = "Member", description = "회원 API") -public interface MemberApiSpec { - - @Operation( - summary = "회원 등록", - description = "PIN 번호를 암호화하여 임시 회원을 생성합니다. 인증번호 검증 후 발급받은 토큰을 통해 인증된 사용자만 접근 가능합니다.") - boolean registerTempMember(HttpServletRequest request, HttpServletResponse response, - @Valid @RequestBody MemberDto.RegisterRequest registerRequest); - - @Operation( - summary = "임시회원의 최초 정보 등록", - description = "임시회원(ROLE_APPLY)이 이름과 전화번호를 처음 등록할 때 사용합니다.") - void registerInitialProfile(@AuthPrincipal Long memberId, - @Valid @RequestBody MemberDto.InitialProfileRequest request); - - @Operation( - summary = "PIN 번호 재설정", - description = "PIN 번호를 재설정합니다.") - void resetPin(@AuthPrincipal Long memberId, - @Valid @RequestBody MemberDto.UpdatePinRequest request); - - @Operation( - summary = "프로필 정보 최초 등록 여부 확인", - description = "임시회원의 최초 프로필 정보(이름, 전화번호) 등록 여부를 확인합니다.") - boolean isInitialMember(@AuthPrincipal Long memberId); - - @Operation( - summary = "현재 로그인한 회원 프로필 조회", - description = "현재 로그인한 회원의 프로필를 조회합니다.") - MemberProfileResponse getCurrentMember(@AuthPrincipal Long memberId); -} diff --git a/src/main/java/org/ject/support/domain/member/controller/MemberController.java b/src/main/java/org/ject/support/domain/member/controller/MemberController.java deleted file mode 100644 index 89bbec49..00000000 --- a/src/main/java/org/ject/support/domain/member/controller/MemberController.java +++ /dev/null @@ -1,97 +0,0 @@ -package org.ject.support.domain.member.controller; - -import jakarta.servlet.http.HttpServletRequest; -import jakarta.servlet.http.HttpServletResponse; -import jakarta.validation.Valid; -import lombok.RequiredArgsConstructor; -import org.ject.support.common.security.AuthPrincipal; -import org.ject.support.common.security.CustomSuccessHandler; -import org.ject.support.common.security.jwt.JwtTokenProvider; -import org.ject.support.domain.member.dto.MemberDto; -import org.ject.support.domain.member.dto.MemberProfileResponse; -import org.ject.support.domain.member.service.MemberService; -import org.springframework.security.access.prepost.PreAuthorize; -import org.springframework.security.core.Authentication; -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.PostMapping; -import org.springframework.web.bind.annotation.PutMapping; -import org.springframework.web.bind.annotation.RequestBody; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RestController; - -@RestController -@RequestMapping("/members") -@RequiredArgsConstructor -public class MemberController implements MemberApiSpec { - - private final MemberService memberService; - private final JwtTokenProvider jwtTokenProvider; - private final CustomSuccessHandler customSuccessHandler; - - - /** - * 회원 등록 API - * 인증번호 검증 후 발급받은 토큰을 통해 인증된 사용자만 접근 가능합니다. - * PIN 번호를 암호화하여 임시 회원을 생성합니다. - */ - @Override - @PostMapping("/apply") - @PreAuthorize("hasRole('ROLE_VERIFICATION')") - public boolean registerTempMember(HttpServletRequest request, HttpServletResponse response, - @Valid @RequestBody MemberDto.RegisterRequest registerRequest) { - - // 쿠키에서 verification 토큰 추출 - String token = jwtTokenProvider.resolveVerificationToken(request); - - // 토큰에서 이메일 추출 - String email = jwtTokenProvider.extractEmailFromVerificationToken(token); - - // 임시 회원 생성 및 토큰 발급 - Authentication authentication = memberService.registerTempMember(registerRequest, email); - customSuccessHandler.onAuthenticationSuccess(request, response, authentication); - - // verification 토큰은 더 이상 필요 없으므로 삭제 - jwtTokenProvider.deleteVerificationCookie(response); - - return true; - } - - /** - * 임시회원의 최초 정보 등록 API - * 임시회원(ROLE_APPLY)이 이름과 전화번호를 처음 등록할 때 사용합니다. - */ - @Override - @PutMapping("/profile/initial") - @PreAuthorize("hasRole('ROLE_APPLY')") - public void registerInitialProfile(@AuthPrincipal Long memberId, - @Valid @RequestBody MemberDto.InitialProfileRequest request) { - - // 임시회원의 최초 프로필 정보 등록 - memberService.registerInitialProfile(request, memberId); - } - - @Override - @PutMapping("/pin") - @PreAuthorize("hasRole('ROLE_APPLY')") - public void resetPin(@AuthPrincipal Long memberId, - @Valid @RequestBody MemberDto.UpdatePinRequest request) { - - // 임시회원의 PIN 번호 재설정 - memberService.updatePin(request, memberId); - } - - @Override - @GetMapping("/profile/initial/status") - @PreAuthorize("hasRole('ROLE_APPLY')") - public boolean isInitialMember(@AuthPrincipal Long memberId) { - // 임시회원의 최초 프로필 정보 등록 여부 확인 - return memberService.checkIsInitialed(memberId); - } - - @Override - @GetMapping("/me") - @PreAuthorize("hasRole('ROLE_APPLY') or hasRole('ROLE_SEMESTER')") - public MemberProfileResponse getCurrentMember(@AuthPrincipal Long memberId) { - return memberService.getMemberProfile(memberId); - } -} diff --git a/src/main/java/org/ject/support/domain/member/dto/MemberDto.java b/src/main/java/org/ject/support/domain/member/dto/MemberDto.java deleted file mode 100644 index e488bae4..00000000 --- a/src/main/java/org/ject/support/domain/member/dto/MemberDto.java +++ /dev/null @@ -1,42 +0,0 @@ -package org.ject.support.domain.member.dto; - -import jakarta.validation.constraints.NotBlank; -import jakarta.validation.constraints.Pattern; -import org.ject.support.domain.member.MemberStatus; -import org.ject.support.domain.member.Role; -import org.ject.support.domain.member.entity.Member; - -public class MemberDto { - - public record RegisterRequest( - - @NotBlank @Pattern(regexp = "^\\d{6}$", message = "PIN 번호는 6자리 숫자여야 합니다.") String pin) { - - public Member toEntity(Long semesterId, String email, String encodedPin) { - return Member.builder() - .semesterId(semesterId) - .email(email) - .pin(encodedPin) - .status(MemberStatus.ACTIVE) - .role(Role.APPLY) - .build(); - } - } - - /** - * 임시회원이 최초로 이름과 전화번호를 등록할 때 사용하는 DTO - */ - public record InitialProfileRequest( - - @NotBlank @Pattern(regexp = "^[가-힣]{1,5}$", message = "한글 1~5글자만 입력 가능합니다.") - String name, - - @NotBlank @Pattern(regexp = "^010\\d{8}$", message = "010으로 시작하는 11자리 숫자를 입력하세요.") - String phoneNumber) { - - } - - public record UpdatePinRequest( - @NotBlank @Pattern(regexp = "^\\d{6}$", message = "PIN 번호는 6자리 숫자여야 합니다.") String pin) { - } -} diff --git a/src/main/java/org/ject/support/domain/member/dto/MemberProfileResponse.java b/src/main/java/org/ject/support/domain/member/dto/MemberProfileResponse.java deleted file mode 100644 index 8f54ba3b..00000000 --- a/src/main/java/org/ject/support/domain/member/dto/MemberProfileResponse.java +++ /dev/null @@ -1,32 +0,0 @@ -package org.ject.support.domain.member.dto; - -import lombok.Builder; -import org.ject.support.domain.member.CareerDetails; -import org.ject.support.domain.member.ExperiencePeriod; -import org.ject.support.domain.member.Region; -import org.ject.support.domain.member.entity.Member; - -import java.util.List; - -@Builder -public record MemberProfileResponse( - Long id, - String name, - String phoneNumber, - CareerDetails careerDetails, - Region region, - ExperiencePeriod experiencePeriod, - List interestedDomains -) { - public static MemberProfileResponse of(Member member) { - return MemberProfileResponse.builder() - .id(member.getId()) - .name(member.getName()) - .phoneNumber(member.getPhoneNumber()) - .careerDetails(member.getCareerDetails()) - .region(member.getRegion()) - .experiencePeriod(member.getExperiencePeriod()) - .interestedDomains(member.getInterestedDomains()) - .build(); - } -} diff --git a/src/main/java/org/ject/support/domain/member/service/MemberService.java b/src/main/java/org/ject/support/domain/member/service/MemberService.java deleted file mode 100644 index c0d6c6e6..00000000 --- a/src/main/java/org/ject/support/domain/member/service/MemberService.java +++ /dev/null @@ -1,106 +0,0 @@ -package org.ject.support.domain.member.service; - -import lombok.RequiredArgsConstructor; -import org.ject.support.common.security.jwt.JwtTokenProvider; -import org.ject.support.domain.member.dto.MemberDto; -import org.ject.support.domain.member.dto.MemberDto.RegisterRequest; -import org.ject.support.domain.member.dto.MemberDto.UpdatePinRequest; -import org.ject.support.domain.member.dto.MemberProfileResponse; -import org.ject.support.domain.member.entity.Member; -import org.ject.support.domain.member.exception.MemberErrorCode; -import org.ject.support.domain.member.exception.MemberException; -import org.ject.support.domain.member.repository.MemberRepository; -import org.ject.support.domain.recruit.domain.Semester; -import org.ject.support.domain.recruit.exception.SemesterErrorCode; -import org.ject.support.domain.recruit.exception.SemesterException; -import org.ject.support.domain.recruit.repository.SemesterRepository; -import org.springframework.security.core.Authentication; -import org.springframework.security.crypto.password.PasswordEncoder; -import org.springframework.stereotype.Service; -import org.springframework.transaction.annotation.Transactional; - -import static org.ject.support.domain.member.exception.MemberErrorCode.ALREADY_EXIST_MEMBER; - -@Service -@RequiredArgsConstructor -public class MemberService { - - private final MemberRepository memberRepository; - private final SemesterRepository semesterRepository; - private final JwtTokenProvider jwtTokenProvider; - private final PasswordEncoder passwordEncoder; - - /** - * 인증된 사용자의 PIN 번호를 설정하고 임시 회원을 생성합니다. - * 인증번호 검증 -> PIN 번호 설정 -> 임시 회원 생성 단계에서 호출됩니다. - * 인증 토큰을 통해 검증된 이메일로 회원을 조회하고, PIN 번호를 암호화하여 저장합니다. - */ - @Transactional - public Authentication registerTempMember(RegisterRequest registerRequest, String email) { - // 이메일로 회원 조회 - Member member = memberRepository.findByEmail(email) - .orElse(null); - - if (member == null) { - // 새로운 회원 생성 - member = createTempMemberWithPin(registerRequest, email); - } else { - // 기존 회원이 있는 경우 PIN 번호만 업데이트 - throw new MemberException(ALREADY_EXIST_MEMBER); - } - - // 인증 및 토큰 발급 - return jwtTokenProvider.createAuthenticationByMember(member); - } - - /** - * PIN 번호가 설정된 임시 회원을 생성합니다. - * PIN 번호는 암호화하여 저장합니다. - */ - private Member createTempMemberWithPin(RegisterRequest registerRequest, String email) { - String encodedPin = passwordEncoder.encode(registerRequest.pin()); - - Semester semester = semesterRepository.findRecruitingSemester() - .orElseThrow(() -> new SemesterException(SemesterErrorCode.NOT_FOUND_RECRUITING_SEMESTER)); - - Member member = registerRequest.toEntity(semester.getId(), email, encodedPin); - - return memberRepository.save(member); - } - - /** - * 임시회원의 최초 프로필 정보(이름, 전화번호) 등록 - * 임시회원(ROLE_APPLY)이 최초로 이름과 전화번호를 등록할 때 호출됩니다. - */ - @Transactional - public void registerInitialProfile(MemberDto.InitialProfileRequest request, Long memberId) { - Member member = memberRepository.findById(memberId) - .orElseThrow(() -> new MemberException(MemberErrorCode.NOT_FOUND_MEMBER)); - - member.updateNameAndPhoneNumber(request.name(), request.phoneNumber()); - } - - @Transactional - public void updatePin(UpdatePinRequest request, Long memberId) { - Member member = memberRepository.findById(memberId) - .orElseThrow(() -> new MemberException(MemberErrorCode.NOT_FOUND_MEMBER)); - - String encodedPin = passwordEncoder.encode(request.pin()); - member.updatePin(encodedPin); - } - - @Transactional(readOnly = true) - public boolean checkIsInitialed(Long memberId) { - Member member = memberRepository.findById(memberId) - .orElseThrow(() -> new MemberException(MemberErrorCode.NOT_FOUND_MEMBER)); - - return member.isInitialed(); - } - - @Transactional(readOnly = true) - public MemberProfileResponse getMemberProfile(Long memberId) { - Member member = memberRepository.findById(memberId) - .orElseThrow(() -> new MemberException(MemberErrorCode.NOT_FOUND_MEMBER)); - return MemberProfileResponse.of(member); - } -} diff --git a/src/test/java/org/ject/support/admin/account/controller/AdminAccountControllerTest.java b/src/test/java/org/ject/support/admin/account/controller/AdminAccountControllerTest.java index 46ee2fbd..e89a0935 100644 --- a/src/test/java/org/ject/support/admin/account/controller/AdminAccountControllerTest.java +++ b/src/test/java/org/ject/support/admin/account/controller/AdminAccountControllerTest.java @@ -10,7 +10,7 @@ import org.ject.support.base.UnitTestSupport; import org.ject.support.common.exception.GlobalErrorCode; import org.ject.support.common.exception.GlobalExceptionHandler; -import org.ject.support.common.security.AuthenticatedMemberIdResolver; +import org.ject.support.common.security.AuthenticatedApplicantIdResolver; import org.ject.support.common.security.CustomUserDetails; import org.ject.support.domain.member.MemberStatus; import org.ject.support.domain.member.Role; @@ -63,7 +63,7 @@ void setUp() { mockMvc = MockMvcBuilders.standaloneSetup(adminAccountController) .setControllerAdvice(new GlobalExceptionHandler()) .setCustomArgumentResolvers( - new AuthenticatedMemberIdResolver(), + new AuthenticatedApplicantIdResolver(), new PageableHandlerMethodArgumentResolver()) .build(); } diff --git a/src/test/java/org/ject/support/admin/account/service/AdminAccountServiceTest.java b/src/test/java/org/ject/support/admin/account/service/AdminAccountServiceTest.java index 9b8373e3..40d57b23 100644 --- a/src/test/java/org/ject/support/admin/account/service/AdminAccountServiceTest.java +++ b/src/test/java/org/ject/support/admin/account/service/AdminAccountServiceTest.java @@ -10,12 +10,12 @@ import org.ject.support.admin.exception.AdminErrorCode; import org.ject.support.admin.exception.AdminException; import org.ject.support.base.UnitTestSupport; +import org.ject.support.domain.applicant.dto.ApplicantAccountProjection; +import org.ject.support.domain.applicant.entity.Applicant; +import org.ject.support.domain.applicant.repository.ApplicantRepository; import org.ject.support.domain.member.MemberType; import org.ject.support.domain.member.MemberStatus; import org.ject.support.domain.member.Role; -import org.ject.support.domain.member.dto.MemberAccountProjection; -import org.ject.support.domain.member.entity.Member; -import org.ject.support.domain.member.repository.MemberRepository; import org.junit.jupiter.api.Test; import org.mockito.ArgumentCaptor; import org.mockito.InjectMocks; @@ -41,7 +41,7 @@ class AdminAccountServiceTest extends UnitTestSupport { private AdminAccountService adminAccountService; @Mock - private MemberRepository memberRepository; + private ApplicantRepository applicantRepository; @Mock private AdminMemberComponent adminMemberComponent; @@ -60,10 +60,10 @@ class AdminAccountServiceTest extends UnitTestSupport { var condition = new AdminAccountSearchCondition( List.of(Role.ADMIN, Role.SUPPORTER), List.of(MemberStatus.ACTIVE)); - var projection = new MemberAccountProjection(1L, TEST_EMAIL, "김젝트", Role.ADMIN, MemberStatus.ACTIVE); + var projection = new ApplicantAccountProjection(1L, TEST_EMAIL, "김젝트", Role.ADMIN, MemberStatus.ACTIVE); var page = new PageImpl<>(List.of(projection), pageable, 1); - given(memberRepository.findAccounts(condition, pageable)).willReturn(page); + given(applicantRepository.findAccounts(condition, pageable)).willReturn(page); // when var result = adminAccountService.findAccounts(condition, pageable); @@ -71,7 +71,7 @@ class AdminAccountServiceTest extends UnitTestSupport { // then assertThat(result.getContent()) .containsExactly(new AdminAccountResponse(1L, TEST_EMAIL, "김젝트", Role.ADMIN, MemberStatus.ACTIVE)); - verify(memberRepository).findAccounts(condition, pageable); + verify(applicantRepository).findAccounts(condition, pageable); } @Test @@ -88,7 +88,7 @@ class AdminAccountServiceTest extends UnitTestSupport { .extracting(e -> ((AdminException) e).getErrorCode()) .isEqualTo(AdminErrorCode.INVALID_ADMIN_ACCOUNT_ROLE); - verify(memberRepository, never()).findAccounts(any(), eq(pageable)); + verify(applicantRepository, never()).findAccounts(any(), eq(pageable)); } @Test @@ -96,25 +96,25 @@ class AdminAccountServiceTest extends UnitTestSupport { // given var request = new AdminAccountCreateRequest(TEST_EMAIL, TEST_PASSWORD, "김젝트", Role.OPERATIONS); - given(memberRepository.existsByEmail(TEST_EMAIL)).willReturn(false); + given(applicantRepository.existsByEmail(TEST_EMAIL)).willReturn(false); given(passwordEncoder.encode(TEST_PASSWORD)).willReturn(ENCODED_PASSWORD); // when adminAccountService.createAccount(request); // then - var memberCaptor = ArgumentCaptor.forClass(Member.class); - verify(memberRepository).existsByEmail(TEST_EMAIL); + var applicantCaptor = ArgumentCaptor.forClass(Applicant.class); + verify(applicantRepository).existsByEmail(TEST_EMAIL); verify(passwordEncoder).encode(TEST_PASSWORD); - verify(memberRepository).save(memberCaptor.capture()); - - Member savedMember = memberCaptor.getValue(); - assertThat(savedMember.getEmail()).isEqualTo(TEST_EMAIL); - assertThat(savedMember.getPin()).isEqualTo(ENCODED_PASSWORD); - assertThat(savedMember.getName()).isEqualTo("김젝트"); - assertThat(savedMember.getRole()).isEqualTo(Role.OPERATIONS); - assertThat(savedMember.getMemberType()).isEqualTo(MemberType.SEMESTER); - assertThat(savedMember.getSemesterId()).isEqualTo(1L); + verify(applicantRepository).save(applicantCaptor.capture()); + + Applicant savedApplicant = applicantCaptor.getValue(); + assertThat(savedApplicant.getEmail()).isEqualTo(TEST_EMAIL); + assertThat(savedApplicant.getPin()).isEqualTo(ENCODED_PASSWORD); + assertThat(savedApplicant.getName()).isEqualTo("김젝트"); + assertThat(savedApplicant.getRole()).isEqualTo(Role.OPERATIONS); + assertThat(savedApplicant.getMemberType()).isEqualTo(MemberType.SEMESTER); + assertThat(savedApplicant.getSemesterId()).isEqualTo(1L); } @Test @@ -122,17 +122,17 @@ class AdminAccountServiceTest extends UnitTestSupport { // given var request = new AdminAccountCreateRequest(TEST_EMAIL, TEST_PASSWORD, "", Role.ADMIN); - given(memberRepository.existsByEmail(TEST_EMAIL)).willReturn(false); + given(applicantRepository.existsByEmail(TEST_EMAIL)).willReturn(false); given(passwordEncoder.encode(TEST_PASSWORD)).willReturn(ENCODED_PASSWORD); // when adminAccountService.createAccount(request); // then - var memberCaptor = ArgumentCaptor.forClass(Member.class); - verify(memberRepository).save(memberCaptor.capture()); + var applicantCaptor = ArgumentCaptor.forClass(Applicant.class); + verify(applicantRepository).save(applicantCaptor.capture()); - assertThat(memberCaptor.getValue().getName()).isNull(); + assertThat(applicantCaptor.getValue().getName()).isNull(); } @Test @@ -140,17 +140,17 @@ class AdminAccountServiceTest extends UnitTestSupport { // given var request = new AdminAccountCreateRequest(TEST_EMAIL, TEST_PASSWORD, " ", Role.ADMIN); - given(memberRepository.existsByEmail(TEST_EMAIL)).willReturn(false); + given(applicantRepository.existsByEmail(TEST_EMAIL)).willReturn(false); given(passwordEncoder.encode(TEST_PASSWORD)).willReturn(ENCODED_PASSWORD); // when adminAccountService.createAccount(request); // then - var memberCaptor = ArgumentCaptor.forClass(Member.class); - verify(memberRepository).save(memberCaptor.capture()); + var applicantCaptor = ArgumentCaptor.forClass(Applicant.class); + verify(applicantRepository).save(applicantCaptor.capture()); - assertThat(memberCaptor.getValue().getName()).isNull(); + assertThat(applicantCaptor.getValue().getName()).isNull(); } @Test @@ -158,7 +158,7 @@ class AdminAccountServiceTest extends UnitTestSupport { // given var request = new AdminAccountCreateRequest(TEST_EMAIL, TEST_PASSWORD, "김젝트", Role.ADMIN); - given(memberRepository.existsByEmail(TEST_EMAIL)).willReturn(true); + given(applicantRepository.existsByEmail(TEST_EMAIL)).willReturn(true); // expected assertThatThrownBy(() -> adminAccountService.createAccount(request)) @@ -166,9 +166,9 @@ class AdminAccountServiceTest extends UnitTestSupport { .extracting(e -> ((AdminException) e).getErrorCode()) .isEqualTo(AdminErrorCode.DUPLICATE_ADMIN_EMAIL); - verify(memberRepository).existsByEmail(TEST_EMAIL); + verify(applicantRepository).existsByEmail(TEST_EMAIL); verify(passwordEncoder, never()).encode(anyString()); - verify(memberRepository, never()).save(org.mockito.ArgumentMatchers.any(Member.class)); + verify(applicantRepository, never()).save(org.mockito.ArgumentMatchers.any(Applicant.class)); } @Test @@ -182,9 +182,9 @@ class AdminAccountServiceTest extends UnitTestSupport { .extracting(e -> ((AdminException) e).getErrorCode()) .isEqualTo(AdminErrorCode.INVALID_ADMIN_ACCOUNT_ROLE); - verify(memberRepository, never()).existsByEmail(anyString()); + verify(applicantRepository, never()).existsByEmail(anyString()); verify(passwordEncoder, never()).encode(anyString()); - verify(memberRepository, never()).save(org.mockito.ArgumentMatchers.any(Member.class)); + verify(applicantRepository, never()).save(org.mockito.ArgumentMatchers.any(Applicant.class)); } @Test @@ -193,7 +193,7 @@ class AdminAccountServiceTest extends UnitTestSupport { var requesterId = 2L; var memberId = 1L; var request = new AdminAccountUpdateRequest("이젝트", Role.SUPPORTER, false); - var member = Member.builder() + var applicant = Applicant.builder() .id(memberId) .email(TEST_EMAIL) .name("김젝트") @@ -203,20 +203,20 @@ class AdminAccountServiceTest extends UnitTestSupport { .semesterId(1L) .build(); - given(adminMemberComponent.getRequiredBackofficeMemberById(memberId)).willReturn(member); + given(adminMemberComponent.getRequiredBackofficeMemberById(memberId)).willReturn(applicant); // when adminAccountService.updateAccount(requesterId, memberId, request); // then verify(adminMemberComponent).getRequiredBackofficeMemberById(memberId); - verify(memberRepository, never()).findByEmail(anyString()); - assertThat(member.getEmail()).isEqualTo(TEST_EMAIL); - assertThat(member.getName()).isEqualTo("이젝트"); - assertThat(member.getRole()).isEqualTo(Role.SUPPORTER); - assertThat(member.getStatus()).isEqualTo(MemberStatus.LOCKED); - assertThat(member.getPhoneNumber()).isEqualTo("01012345678"); - assertThat(member.getSemesterId()).isEqualTo(1L); + verify(applicantRepository, never()).findByEmail(anyString()); + assertThat(applicant.getEmail()).isEqualTo(TEST_EMAIL); + assertThat(applicant.getName()).isEqualTo("이젝트"); + assertThat(applicant.getRole()).isEqualTo(Role.SUPPORTER); + assertThat(applicant.getStatus()).isEqualTo(MemberStatus.LOCKED); + assertThat(applicant.getPhoneNumber()).isEqualTo("01012345678"); + assertThat(applicant.getSemesterId()).isEqualTo(1L); } @Test @@ -225,7 +225,7 @@ class AdminAccountServiceTest extends UnitTestSupport { var requesterId = 2L; var memberId = 1L; var request = new AdminAccountUpdateRequest(" ", Role.ADMIN, true); - var member = Member.builder() + var applicant = Applicant.builder() .id(memberId) .email(TEST_EMAIL) .name("김젝트") @@ -234,16 +234,16 @@ class AdminAccountServiceTest extends UnitTestSupport { .semesterId(1L) .build(); - given(adminMemberComponent.getRequiredBackofficeMemberById(memberId)).willReturn(member); + given(adminMemberComponent.getRequiredBackofficeMemberById(memberId)).willReturn(applicant); // when adminAccountService.updateAccount(requesterId, memberId, request); // then - assertThat(member.getName()).isNull(); - assertThat(member.getEmail()).isEqualTo(TEST_EMAIL); - assertThat(member.getRole()).isEqualTo(Role.ADMIN); - assertThat(member.getStatus()).isEqualTo(MemberStatus.ACTIVE); + assertThat(applicant.getName()).isNull(); + assertThat(applicant.getEmail()).isEqualTo(TEST_EMAIL); + assertThat(applicant.getRole()).isEqualTo(Role.ADMIN); + assertThat(applicant.getStatus()).isEqualTo(MemberStatus.ACTIVE); } @Test @@ -258,7 +258,7 @@ class AdminAccountServiceTest extends UnitTestSupport { .isEqualTo(AdminErrorCode.INVALID_ADMIN_ACCOUNT_ROLE); verify(adminMemberComponent, never()).getRequiredBackofficeMemberById(org.mockito.ArgumentMatchers.anyLong()); - verify(memberRepository, never()).findByEmail(anyString()); + verify(applicantRepository, never()).findByEmail(anyString()); } @Test @@ -273,7 +273,7 @@ class AdminAccountServiceTest extends UnitTestSupport { .isEqualTo(AdminErrorCode.CANNOT_LOCK_SELF); verify(adminMemberComponent, never()).getRequiredBackofficeMemberById(org.mockito.ArgumentMatchers.anyLong()); - verify(memberRepository, never()).findByEmail(anyString()); + verify(applicantRepository, never()).findByEmail(anyString()); } @Test @@ -288,7 +288,7 @@ class AdminAccountServiceTest extends UnitTestSupport { .isEqualTo(AdminErrorCode.CANNOT_CHANGE_OWN_ADMIN_ROLE); verify(adminMemberComponent, never()).getRequiredBackofficeMemberById(org.mockito.ArgumentMatchers.anyLong()); - verify(memberRepository, never()).findByEmail(anyString()); + verify(applicantRepository, never()).findByEmail(anyString()); } @Test @@ -296,7 +296,7 @@ class AdminAccountServiceTest extends UnitTestSupport { // given var memberId = 1L; var request = new AdminAccountRoleUpdateRequest(Role.SUPPORTER); - var member = Member.builder() + var applicant = Applicant.builder() .id(memberId) .email(TEST_EMAIL) .name("김젝트") @@ -305,18 +305,18 @@ class AdminAccountServiceTest extends UnitTestSupport { .semesterId(1L) .build(); - given(adminMemberComponent.getRequiredBackofficeMemberById(memberId)).willReturn(member); + given(adminMemberComponent.getRequiredBackofficeMemberById(memberId)).willReturn(applicant); // when adminAccountService.updateRole(memberId, request); // then verify(adminMemberComponent).getRequiredBackofficeMemberById(memberId); - assertThat(member.getRole()).isEqualTo(Role.SUPPORTER); - assertThat(member.getEmail()).isEqualTo(TEST_EMAIL); - assertThat(member.getName()).isEqualTo("김젝트"); - assertThat(member.getPhoneNumber()).isEqualTo("01012345678"); - assertThat(member.getSemesterId()).isEqualTo(1L); + assertThat(applicant.getRole()).isEqualTo(Role.SUPPORTER); + assertThat(applicant.getEmail()).isEqualTo(TEST_EMAIL); + assertThat(applicant.getName()).isEqualTo("김젝트"); + assertThat(applicant.getPhoneNumber()).isEqualTo("01012345678"); + assertThat(applicant.getSemesterId()).isEqualTo(1L); } @Test @@ -357,7 +357,7 @@ class AdminAccountServiceTest extends UnitTestSupport { var requesterId = 2L; var memberId = 1L; var request = new AdminAccountActiveUpdateRequest(false); - var member = Member.builder() + var applicant = Applicant.builder() .id(memberId) .email(TEST_EMAIL) .role(Role.OPERATIONS) @@ -365,14 +365,14 @@ class AdminAccountServiceTest extends UnitTestSupport { .semesterId(1L) .build(); - given(adminMemberComponent.getRequiredBackofficeMemberById(memberId)).willReturn(member); + given(adminMemberComponent.getRequiredBackofficeMemberById(memberId)).willReturn(applicant); // when adminAccountService.updateActive(requesterId, memberId, request); // then verify(adminMemberComponent).getRequiredBackofficeMemberById(memberId); - verify(adminMemberComponent).changeMemberStatus(eq(member), eq(MemberStatus.LOCKED)); + verify(adminMemberComponent).changeMemberStatus(eq(applicant), eq(MemberStatus.LOCKED)); } @Test @@ -381,7 +381,7 @@ class AdminAccountServiceTest extends UnitTestSupport { var requesterId = 2L; var memberId = 1L; var request = new AdminAccountActiveUpdateRequest(true); - var member = Member.builder() + var applicant = Applicant.builder() .id(memberId) .email(TEST_EMAIL) .role(Role.OPERATIONS) @@ -389,14 +389,14 @@ class AdminAccountServiceTest extends UnitTestSupport { .semesterId(1L) .build(); - given(adminMemberComponent.getRequiredBackofficeMemberById(memberId)).willReturn(member); + given(adminMemberComponent.getRequiredBackofficeMemberById(memberId)).willReturn(applicant); // when adminAccountService.updateActive(requesterId, memberId, request); // then verify(adminMemberComponent).getRequiredBackofficeMemberById(memberId); - verify(adminMemberComponent).changeMemberStatus(eq(member), eq(MemberStatus.ACTIVE)); + verify(adminMemberComponent).changeMemberStatus(eq(applicant), eq(MemberStatus.ACTIVE)); } @Test diff --git a/src/test/java/org/ject/support/admin/auth/service/AdminAuthServiceTest.java b/src/test/java/org/ject/support/admin/auth/service/AdminAuthServiceTest.java index a9814317..03fff332 100644 --- a/src/test/java/org/ject/support/admin/auth/service/AdminAuthServiceTest.java +++ b/src/test/java/org/ject/support/admin/auth/service/AdminAuthServiceTest.java @@ -5,9 +5,9 @@ import org.ject.support.admin.exception.AdminException; import org.ject.support.admin.component.AdminMemberComponent; import org.ject.support.common.security.jwt.JwtTokenProvider; +import org.ject.support.domain.applicant.entity.Applicant; import org.ject.support.domain.member.MemberStatus; import org.ject.support.domain.member.Role; -import org.ject.support.domain.member.entity.Member; import org.junit.jupiter.api.Test; import org.mockito.InjectMocks; import org.mockito.Mock; @@ -56,7 +56,7 @@ class AdminAuthServiceTest extends UnitTestSupport { String password = "Password123"; String failCountKey = "admin-login-password-fail-count:" + email; String blockKey = "admin-login-password-block:" + email; - Member adminMember = Member.builder() + Applicant adminApplicant = Applicant.builder() .id(1L) .email(email) .pin("$2a$10$encodedPin") @@ -65,9 +65,9 @@ class AdminAuthServiceTest extends UnitTestSupport { .build(); given(redisTemplate.hasKey(blockKey)).willReturn(false); - given(adminMemberComponent.findBackofficeMemberByEmail(email)).willReturn(Optional.of(adminMember)); - given(passwordEncoder.matches(password, adminMember.getPin())).willReturn(true); - given(jwtTokenProvider.createAuthenticationByMember(adminMember)).willReturn(authentication); + given(adminMemberComponent.findBackofficeMemberByEmail(email)).willReturn(Optional.of(adminApplicant)); + given(passwordEncoder.matches(password, adminApplicant.getPin())).willReturn(true); + given(jwtTokenProvider.createAuthenticationByApplicant(adminApplicant)).willReturn(authentication); // when adminAuthService.authenticateAdmin(email, password); @@ -75,7 +75,7 @@ class AdminAuthServiceTest extends UnitTestSupport { // then verify(redisTemplate).delete(failCountKey); verify(redisTemplate).delete(blockKey); - verify(jwtTokenProvider).createAuthenticationByMember(adminMember); + verify(jwtTokenProvider).createAuthenticationByApplicant(adminApplicant); } @Test @@ -85,7 +85,7 @@ class AdminAuthServiceTest extends UnitTestSupport { String password = "Password123"; String failCountKey = "admin-login-password-fail-count:" + email; String blockKey = "admin-login-password-block:" + email; - Member operationsMember = Member.builder() + Applicant operationsApplicant = Applicant.builder() .id(2L) .email(email) .pin("$2a$10$encodedPin") @@ -94,9 +94,9 @@ class AdminAuthServiceTest extends UnitTestSupport { .build(); given(redisTemplate.hasKey(blockKey)).willReturn(false); - given(adminMemberComponent.findBackofficeMemberByEmail(email)).willReturn(Optional.of(operationsMember)); - given(passwordEncoder.matches(password, operationsMember.getPin())).willReturn(true); - given(jwtTokenProvider.createAuthenticationByMember(operationsMember)).willReturn(authentication); + given(adminMemberComponent.findBackofficeMemberByEmail(email)).willReturn(Optional.of(operationsApplicant)); + given(passwordEncoder.matches(password, operationsApplicant.getPin())).willReturn(true); + given(jwtTokenProvider.createAuthenticationByApplicant(operationsApplicant)).willReturn(authentication); // when adminAuthService.authenticateAdmin(email, password); @@ -104,7 +104,7 @@ class AdminAuthServiceTest extends UnitTestSupport { // then verify(redisTemplate).delete(failCountKey); verify(redisTemplate).delete(blockKey); - verify(jwtTokenProvider).createAuthenticationByMember(operationsMember); + verify(jwtTokenProvider).createAuthenticationByApplicant(operationsApplicant); } @Test @@ -130,7 +130,7 @@ class AdminAuthServiceTest extends UnitTestSupport { // given String email = "admin@ject.org"; String password = "WrongPassword123"; - Member adminMember = Member.builder() + Applicant adminApplicant = Applicant.builder() .id(1L) .email(email) .pin("$2a$10$encodedPin") @@ -139,11 +139,11 @@ class AdminAuthServiceTest extends UnitTestSupport { .build(); String failCountKey = "admin-login-password-fail-count:" + email; - given(adminMemberComponent.findBackofficeMemberByEmail(email)).willReturn(Optional.of(adminMember)); + given(adminMemberComponent.findBackofficeMemberByEmail(email)).willReturn(Optional.of(adminApplicant)); given(redisTemplate.hasKey("admin-login-password-block:" + email)).willReturn(false); given(redisTemplate.opsForValue()).willReturn(valueOperations); given(valueOperations.increment(failCountKey)).willReturn(1L); - given(passwordEncoder.matches(password, adminMember.getPin())).willReturn(false); + given(passwordEncoder.matches(password, adminApplicant.getPin())).willReturn(false); // when, then assertThatThrownBy(() -> adminAuthService.authenticateAdmin(email, password)) @@ -157,7 +157,7 @@ class AdminAuthServiceTest extends UnitTestSupport { // given String email = "locked_admin@test.com"; String password = "Password123!"; - Member adminMember = Member.builder() + Applicant adminApplicant = Applicant.builder() .id(1L) .email(email) .pin("$2a$10$encodedPin") @@ -165,9 +165,9 @@ class AdminAuthServiceTest extends UnitTestSupport { .role(Role.ADMIN) .build(); - given(adminMemberComponent.findBackofficeMemberByEmail(email)).willReturn(Optional.of(adminMember)); + given(adminMemberComponent.findBackofficeMemberByEmail(email)).willReturn(Optional.of(adminApplicant)); given(redisTemplate.hasKey("admin-login-password-block:" + email)).willReturn(false); - given(passwordEncoder.matches(password, adminMember.getPin())).willReturn(true); + given(passwordEncoder.matches(password, adminApplicant.getPin())).willReturn(true); // when, then assertThatThrownBy(() -> adminAuthService.authenticateAdmin(email, password)) diff --git a/src/test/java/org/ject/support/admin/component/AdminMemberComponentTest.java b/src/test/java/org/ject/support/admin/component/AdminMemberComponentTest.java index bd65461a..e3011db8 100644 --- a/src/test/java/org/ject/support/admin/component/AdminMemberComponentTest.java +++ b/src/test/java/org/ject/support/admin/component/AdminMemberComponentTest.java @@ -3,10 +3,10 @@ import org.ject.support.admin.exception.AdminErrorCode; import org.ject.support.admin.exception.AdminException; import org.ject.support.base.UnitTestSupport; +import org.ject.support.domain.applicant.entity.Applicant; +import org.ject.support.domain.applicant.repository.ApplicantRepository; import org.ject.support.domain.member.MemberStatus; import org.ject.support.domain.member.Role; -import org.ject.support.domain.member.entity.Member; -import org.ject.support.domain.member.repository.MemberRepository; import org.junit.jupiter.api.Test; import org.mockito.InjectMocks; import org.mockito.Mock; @@ -29,14 +29,14 @@ class AdminMemberComponentTest extends UnitTestSupport { AdminMemberComponent adminMemberComponent; @Mock - MemberRepository memberRepository; + ApplicantRepository applicantRepository; @Test void 존재하지않는_이메일로_관리자계정의_정보를_조회할_경우_NOT_FOUND_ADMIN_예외가_발생() { // given String notFoundEmail = "not_found_admin@test.com"; - given(memberRepository.findByEmailAndRoleIn(notFoundEmail, BACKOFFICE_ROLES)).willReturn(Optional.empty()); + given(applicantRepository.findByEmailAndRoleIn(notFoundEmail, BACKOFFICE_ROLES)).willReturn(Optional.empty()); // when, then assertThatThrownBy(() -> adminMemberComponent.getRequiredBackofficeMemberByEmail(notFoundEmail)) @@ -49,20 +49,20 @@ class AdminMemberComponentTest extends UnitTestSupport { void 이메일로_관리자계정의_정보를_조회할_경우_Member_엔티티를_반환() { // given String email = "admin@test.com"; - Member foundMember = Member.builder() + Applicant foundApplicant = Applicant.builder() .id(1L) .email(email) .status(MemberStatus.ACTIVE) .role(Role.ADMIN) .build(); - given(memberRepository.findByEmailAndRoleIn(email, BACKOFFICE_ROLES)).willReturn(Optional.of(foundMember)); + given(applicantRepository.findByEmailAndRoleIn(email, BACKOFFICE_ROLES)).willReturn(Optional.of(foundApplicant)); // when - Member result = adminMemberComponent.getRequiredBackofficeMemberByEmail(email); + Applicant result = adminMemberComponent.getRequiredBackofficeMemberByEmail(email); // then - verify(memberRepository).findByEmailAndRoleIn(email, BACKOFFICE_ROLES); + verify(applicantRepository).findByEmailAndRoleIn(email, BACKOFFICE_ROLES); assertEquals(email, result.getEmail()); assertEquals(Role.ADMIN, result.getRole()); assertEquals(MemberStatus.ACTIVE, result.getStatus()); @@ -72,20 +72,20 @@ class AdminMemberComponentTest extends UnitTestSupport { void 이메일로_관리자계정의_정보를_Optional로_조회할_경우_존재하면_Member를_반환() { // given String email = "admin@test.com"; - Member foundMember = Member.builder() + Applicant foundApplicant = Applicant.builder() .id(1L) .email(email) .status(MemberStatus.ACTIVE) .role(Role.SUPPORTER) .build(); - given(memberRepository.findByEmailAndRoleIn(email, BACKOFFICE_ROLES)).willReturn(Optional.of(foundMember)); + given(applicantRepository.findByEmailAndRoleIn(email, BACKOFFICE_ROLES)).willReturn(Optional.of(foundApplicant)); // when - Optional result = adminMemberComponent.findBackofficeMemberByEmail(email); + Optional result = adminMemberComponent.findBackofficeMemberByEmail(email); // then - verify(memberRepository).findByEmailAndRoleIn(email, BACKOFFICE_ROLES); + verify(applicantRepository).findByEmailAndRoleIn(email, BACKOFFICE_ROLES); assertThat(result).isPresent(); assertThat(result.get().getEmail()).isEqualTo(email); assertThat(result.get().getRole()).isEqualTo(Role.SUPPORTER); @@ -96,13 +96,13 @@ class AdminMemberComponentTest extends UnitTestSupport { // given String email = "not_found_admin@test.com"; - given(memberRepository.findByEmailAndRoleIn(email, BACKOFFICE_ROLES)).willReturn(Optional.empty()); + given(applicantRepository.findByEmailAndRoleIn(email, BACKOFFICE_ROLES)).willReturn(Optional.empty()); // when - Optional result = adminMemberComponent.findBackofficeMemberByEmail(email); + Optional result = adminMemberComponent.findBackofficeMemberByEmail(email); // then - verify(memberRepository).findByEmailAndRoleIn(email, BACKOFFICE_ROLES); + verify(applicantRepository).findByEmailAndRoleIn(email, BACKOFFICE_ROLES); assertThat(result).isEmpty(); } @@ -110,20 +110,20 @@ class AdminMemberComponentTest extends UnitTestSupport { void id로_관리자계정의_정보를_조회할_경우_Member_엔티티를_반환() { // given Long memberId = 1L; - Member foundMember = Member.builder() + Applicant foundApplicant = Applicant.builder() .id(memberId) .email("admin@test.com") .status(MemberStatus.ACTIVE) .role(Role.ADMIN) .build(); - given(memberRepository.findByIdAndRoleIn(memberId, BACKOFFICE_ROLES)).willReturn(Optional.of(foundMember)); + given(applicantRepository.findByIdAndRoleIn(memberId, BACKOFFICE_ROLES)).willReturn(Optional.of(foundApplicant)); // when - Member result = adminMemberComponent.getRequiredBackofficeMemberById(memberId); + Applicant result = adminMemberComponent.getRequiredBackofficeMemberById(memberId); // then - verify(memberRepository).findByIdAndRoleIn(memberId, BACKOFFICE_ROLES); + verify(applicantRepository).findByIdAndRoleIn(memberId, BACKOFFICE_ROLES); assertEquals(memberId, result.getId()); assertEquals(Role.ADMIN, result.getRole()); assertEquals(MemberStatus.ACTIVE, result.getStatus()); @@ -134,7 +134,7 @@ class AdminMemberComponentTest extends UnitTestSupport { // given Long memberId = 999L; - given(memberRepository.findByIdAndRoleIn(memberId, BACKOFFICE_ROLES)).willReturn(Optional.empty()); + given(applicantRepository.findByIdAndRoleIn(memberId, BACKOFFICE_ROLES)).willReturn(Optional.empty()); // when, then assertThatThrownBy(() -> adminMemberComponent.getRequiredBackofficeMemberById(memberId)) @@ -147,7 +147,7 @@ class AdminMemberComponentTest extends UnitTestSupport { void 회원의_상태를_변경할_경우_변경된_상태로_저장된다() { // given Role adminRole = Role.ADMIN; - Member member = Member.builder() + Applicant applicant = Applicant.builder() .id(1L) .email("test@test.com") .status(MemberStatus.ACTIVE) @@ -156,17 +156,17 @@ class AdminMemberComponentTest extends UnitTestSupport { MemberStatus changeStatus = MemberStatus.LOCKED; // when - adminMemberComponent.changeMemberStatus(member, changeStatus); + adminMemberComponent.changeMemberStatus(applicant, changeStatus); // then - verify(memberRepository).save(member); - assertEquals(changeStatus, member.getStatus()); + verify(applicantRepository).save(applicant); + assertEquals(changeStatus, applicant.getStatus()); } @Test void 회원의_상태가_이미_같으면_저장하지_않는다() { // given - Member member = Member.builder() + Applicant applicant = Applicant.builder() .id(1L) .email("test@test.com") .status(MemberStatus.ACTIVE) @@ -174,10 +174,10 @@ class AdminMemberComponentTest extends UnitTestSupport { .build(); // when - adminMemberComponent.changeMemberStatus(member, MemberStatus.ACTIVE); + adminMemberComponent.changeMemberStatus(applicant, MemberStatus.ACTIVE); // then - verify(memberRepository, never()).save(member); - assertEquals(MemberStatus.ACTIVE, member.getStatus()); + verify(applicantRepository, never()).save(applicant); + assertEquals(MemberStatus.ACTIVE, applicant.getStatus()); } } diff --git a/src/test/java/org/ject/support/common/security/AuthenticatedMemberIdResolverTest.java b/src/test/java/org/ject/support/common/security/AuthenticatedApplicantIdResolverTest.java similarity index 90% rename from src/test/java/org/ject/support/common/security/AuthenticatedMemberIdResolverTest.java rename to src/test/java/org/ject/support/common/security/AuthenticatedApplicantIdResolverTest.java index 925186e5..d63f2053 100644 --- a/src/test/java/org/ject/support/common/security/AuthenticatedMemberIdResolverTest.java +++ b/src/test/java/org/ject/support/common/security/AuthenticatedApplicantIdResolverTest.java @@ -4,6 +4,7 @@ import static org.mockito.Mockito.*; import org.ject.support.common.exception.GlobalException; +import org.junit.jupiter.api.AfterEach; import org.ject.support.domain.member.Role; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.DisplayName; @@ -22,10 +23,10 @@ import org.springframework.web.method.support.ModelAndViewContainer; @ExtendWith(MockitoExtension.class) -class AuthenticatedMemberIdResolverTest { +class AuthenticatedApplicantIdResolverTest { @InjectMocks - private AuthenticatedMemberIdResolver resolver; + private AuthenticatedApplicantIdResolver resolver; @Mock private MethodParameter methodParameter; @@ -42,7 +43,7 @@ class AuthenticatedMemberIdResolverTest { @Mock private SecurityContext securityContext; - private static final Long MEMBER_ID = 1L; + private static final Long APPLICANT_ID = 1L; private static final String EMAIL = "test@example.com"; @BeforeEach @@ -50,6 +51,11 @@ void setUp() { SecurityContextHolder.setContext(securityContext); } + @AfterEach + void tearDown() { + SecurityContextHolder.clearContext(); + } + @Test @DisplayName("@AuthPrincipal이 붙은 Long 타입 파라미터를 지원한다") void supportsParameter() { @@ -79,10 +85,10 @@ void notSupportsParameterWithoutAnnotation() { } @Test - @DisplayName("인증된 사용자의 memberId를 반환한다") + @DisplayName("인증된 사용자의 applicantId를 반환한다") void resolveArgumentWithAuthenticatedUser() { // given - CustomUserDetails userDetails = new CustomUserDetails(EMAIL, MEMBER_ID, Role.SEMESTER); + CustomUserDetails userDetails = new CustomUserDetails(EMAIL, APPLICANT_ID, Role.SEMESTER); Authentication authentication = new UsernamePasswordAuthenticationToken(userDetails, "", userDetails.getAuthorities()); when(securityContext.getAuthentication()).thenReturn(authentication); @@ -90,7 +96,7 @@ void resolveArgumentWithAuthenticatedUser() { Long result = resolver.resolveArgument(methodParameter, mavContainer, webRequest, binderFactory); // then - assertThat(result).isEqualTo(MEMBER_ID); + assertThat(result).isEqualTo(APPLICANT_ID); } @Test diff --git a/src/test/java/org/ject/support/common/security/CustomUserDetailServiceTest.java b/src/test/java/org/ject/support/common/security/CustomUserDetailServiceTest.java index f6ee898d..ef431819 100644 --- a/src/test/java/org/ject/support/common/security/CustomUserDetailServiceTest.java +++ b/src/test/java/org/ject/support/common/security/CustomUserDetailServiceTest.java @@ -1,11 +1,9 @@ package org.ject.support.common.security; -import org.ject.support.domain.member.JobFamily; -import org.ject.support.domain.member.MemberStatus; +import org.ject.support.domain.applicant.entity.Applicant; +import org.ject.support.domain.applicant.exception.ApplicantException; +import org.ject.support.domain.applicant.repository.ApplicantRepository; import org.ject.support.domain.member.Role; -import org.ject.support.domain.member.entity.Member; -import org.ject.support.domain.member.exception.MemberException; -import org.ject.support.domain.member.repository.MemberRepository; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; @@ -28,20 +26,18 @@ class CustomUserDetailServiceTest { private CustomUserDetailService userDetailService; @Mock - private MemberRepository memberRepository; + private ApplicantRepository applicantRepository; - private Member testMember; + private Applicant testApplicant; @BeforeEach void setUp() { - testMember = Member.builder() + testApplicant = Applicant.builder() .id(1L) .email("test@example.com") .name("Test User") .phoneNumber("01012345678") .role(Role.SEMESTER) - .status(MemberStatus.ACTIVE) - .jobFamily(JobFamily.BE) .build(); } @@ -49,25 +45,25 @@ void setUp() { @DisplayName("이메일로 사용자 조회 성공 테스트") void loadUserByUsername_Success() { // given - when(memberRepository.findByEmail(anyString())).thenReturn(Optional.of(testMember)); + when(applicantRepository.findByEmail(anyString())).thenReturn(Optional.of(testApplicant)); // when CustomUserDetails userDetails = userDetailService.loadUserByUsername("test@example.com"); // then assertThat(userDetails).isNotNull(); - assertThat(userDetails.getUsername()).isEqualTo(testMember.getEmail()); - assertThat(userDetails.getMemberId()).isEqualTo(testMember.getId()); + assertThat(userDetails.getUsername()).isEqualTo(testApplicant.getEmail()); + assertThat(userDetails.getApplicantId()).isEqualTo(testApplicant.getId()); } @Test @DisplayName("존재하지 않는 이메일로 조회 시 예외 발생 테스트") void loadUserByUsername_NotFound() { // given - when(memberRepository.findByEmail(anyString())).thenReturn(Optional.empty()); + when(applicantRepository.findByEmail(anyString())).thenReturn(Optional.empty()); // when & then assertThatThrownBy(() -> userDetailService.loadUserByUsername("nonexistent@example.com")) - .isInstanceOf(MemberException.class); + .isInstanceOf(ApplicantException.class); } } diff --git a/src/test/java/org/ject/support/common/security/CustomUserDetailsTest.java b/src/test/java/org/ject/support/common/security/CustomUserDetailsTest.java index c4977f5c..8dbe77c5 100644 --- a/src/test/java/org/ject/support/common/security/CustomUserDetailsTest.java +++ b/src/test/java/org/ject/support/common/security/CustomUserDetailsTest.java @@ -1,11 +1,10 @@ package org.ject.support.common.security; +import org.ject.support.domain.applicant.entity.Applicant; import org.ject.support.domain.member.MemberStatus; import org.ject.support.domain.member.Permission; import org.ject.support.domain.member.Role; import org.ject.support.domain.member.RolePermissions; -import org.ject.support.domain.member.entity.Member; -import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; import org.springframework.security.core.GrantedAuthority; @@ -17,13 +16,13 @@ class CustomUserDetailsTest { private final String TEST_EMAIL = "test@example.com"; - private final Long TEST_MEMBER_ID = 1L; + private final Long TEST_APPLICANT_ID = 1L; @Test void Member_객체로_CustomUserDetails_생성_시_권한에_ROLE_접두사가_추가되는지_확인() { // given - Member member = Member.builder() - .id(TEST_MEMBER_ID) + Applicant applicant = Applicant.builder() + .id(TEST_APPLICANT_ID) .email(TEST_EMAIL) .name("Test User") .phoneNumber("01012345678") @@ -32,7 +31,7 @@ class CustomUserDetailsTest { .build(); // when - CustomUserDetails userDetails = new CustomUserDetails(member); + CustomUserDetails userDetails = new CustomUserDetails(applicant); Collection authorities = userDetails.getAuthorities(); List authorityNames = authorities.stream() .map(GrantedAuthority::getAuthority) @@ -49,7 +48,7 @@ class CustomUserDetailsTest { Role role = Role.SEMESTER; // when - CustomUserDetails userDetails = new CustomUserDetails(TEST_EMAIL, TEST_MEMBER_ID, role); + CustomUserDetails userDetails = new CustomUserDetails(TEST_EMAIL, TEST_APPLICANT_ID, role); Collection authorities = userDetails.getAuthorities(); List authorityNames = authorities.stream() .map(GrantedAuthority::getAuthority) @@ -66,7 +65,7 @@ class CustomUserDetailsTest { Role role = Role.ADMIN; // when - CustomUserDetails userDetails = new CustomUserDetails(TEST_EMAIL, TEST_MEMBER_ID, role); + CustomUserDetails userDetails = new CustomUserDetails(TEST_EMAIL, TEST_APPLICANT_ID, role); Collection authorities = userDetails.getAuthorities(); List authorityNames = authorities.stream() .map(GrantedAuthority::getAuthority) @@ -88,7 +87,7 @@ class CustomUserDetailsTest { Role role = Role.SUPPORTER; // when - CustomUserDetails userDetails = new CustomUserDetails(TEST_EMAIL, TEST_MEMBER_ID, role); + CustomUserDetails userDetails = new CustomUserDetails(TEST_EMAIL, TEST_APPLICANT_ID, role); List authorityNames = userDetails.getAuthorities().stream() .map(GrantedAuthority::getAuthority) .toList(); diff --git a/src/test/java/org/ject/support/common/security/jwt/JwtAuthenticationFilterTest.java b/src/test/java/org/ject/support/common/security/jwt/JwtAuthenticationFilterTest.java index c9965170..68169f1d 100644 --- a/src/test/java/org/ject/support/common/security/jwt/JwtAuthenticationFilterTest.java +++ b/src/test/java/org/ject/support/common/security/jwt/JwtAuthenticationFilterTest.java @@ -4,10 +4,10 @@ import org.ject.support.admin.exception.AdminErrorCode; import org.ject.support.admin.exception.AdminException; import org.ject.support.common.security.CustomUserDetails; +import org.ject.support.domain.applicant.entity.Applicant; +import org.ject.support.domain.applicant.repository.ApplicantRepository; import org.ject.support.domain.member.MemberStatus; import org.ject.support.domain.member.Role; -import org.ject.support.domain.member.entity.Member; -import org.ject.support.domain.member.repository.MemberRepository; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; @@ -37,7 +37,7 @@ class JwtAuthenticationFilterTest { private JwtTokenProvider jwtTokenProvider; @Mock - private MemberRepository memberRepository; + private ApplicantRepository applicantRepository; @Mock private FilterChain filterChain; @@ -55,12 +55,12 @@ void tearDown() { var request = new MockHttpServletRequest(); var response = new MockHttpServletResponse(); var authentication = createAuthentication(1L, Role.ADMIN); - var member = createMember(1L, Role.ADMIN, MemberStatus.ACTIVE); + var applicant = createApplicant(1L, Role.ADMIN, MemberStatus.ACTIVE); given(jwtTokenProvider.resolveAccessToken(request)).willReturn(ACCESS_TOKEN); given(jwtTokenProvider.validateToken(ACCESS_TOKEN)).willReturn(true); given(jwtTokenProvider.getAuthenticationByToken(ACCESS_TOKEN)).willReturn(authentication); - given(memberRepository.findByIdAndRoleIn(1L, Role.backofficeRoles())).willReturn(Optional.of(member)); + given(applicantRepository.findByIdAndRoleIn(1L, Role.backofficeRoles())).willReturn(Optional.of(applicant)); // when jwtAuthenticationFilter.doFilterInternal(request, response, filterChain); @@ -76,12 +76,12 @@ void tearDown() { var request = new MockHttpServletRequest(); var response = new MockHttpServletResponse(); var authentication = createAuthentication(1L, Role.ADMIN); - var member = createMember(1L, Role.ADMIN, MemberStatus.LOCKED); + var applicant = createApplicant(1L, Role.ADMIN, MemberStatus.LOCKED); given(jwtTokenProvider.resolveAccessToken(request)).willReturn(ACCESS_TOKEN); given(jwtTokenProvider.validateToken(ACCESS_TOKEN)).willReturn(true); given(jwtTokenProvider.getAuthenticationByToken(ACCESS_TOKEN)).willReturn(authentication); - given(memberRepository.findByIdAndRoleIn(1L, Role.backofficeRoles())).willReturn(Optional.of(member)); + given(applicantRepository.findByIdAndRoleIn(1L, Role.backofficeRoles())).willReturn(Optional.of(applicant)); // when, then assertThatThrownBy(() -> jwtAuthenticationFilter.doFilterInternal(request, response, filterChain)) @@ -108,22 +108,22 @@ void tearDown() { jwtAuthenticationFilter.doFilterInternal(request, response, filterChain); // then - verify(memberRepository, never()).findByIdAndRoleIn(1L, Role.backofficeRoles()); + verify(applicantRepository, never()).findByIdAndRoleIn(1L, Role.backofficeRoles()); verify(filterChain).doFilter(request, response); assertThat(SecurityContextHolder.getContext().getAuthentication()).isEqualTo(authentication); } - private UsernamePasswordAuthenticationToken createAuthentication(final Long memberId, + private UsernamePasswordAuthenticationToken createAuthentication(final Long applicantId, final Role role) { - CustomUserDetails userDetails = new CustomUserDetails("test@ject.kr", memberId, role); + CustomUserDetails userDetails = new CustomUserDetails("test@ject.kr", applicantId, role); return new UsernamePasswordAuthenticationToken(userDetails, "", userDetails.getAuthorities()); } - private Member createMember(final Long memberId, - final Role role, - final MemberStatus status) { - return Member.builder() - .id(memberId) + private Applicant createApplicant(final Long applicantId, + final Role role, + final MemberStatus status) { + return Applicant.builder() + .id(applicantId) .email("test@ject.kr") .role(role) .status(status) diff --git a/src/test/java/org/ject/support/common/security/jwt/JwtTokenProviderTest.java b/src/test/java/org/ject/support/common/security/jwt/JwtTokenProviderTest.java index 6bc5b736..f586fd67 100644 --- a/src/test/java/org/ject/support/common/security/jwt/JwtTokenProviderTest.java +++ b/src/test/java/org/ject/support/common/security/jwt/JwtTokenProviderTest.java @@ -7,10 +7,10 @@ import org.springframework.mock.web.MockHttpServletResponse; import org.ject.support.common.exception.GlobalException; import org.ject.support.common.security.CustomUserDetails; +import org.ject.support.domain.applicant.entity.Applicant; import org.ject.support.domain.member.JobFamily; import org.ject.support.domain.member.MemberStatus; import org.ject.support.domain.member.Role; -import org.ject.support.domain.member.entity.Member; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.mockito.Mock; @@ -37,7 +37,7 @@ class JwtTokenProviderTest extends UnitTestSupport { @Mock private Environment environment; - private Member testMember; + private Applicant testApplicant; private Authentication authentication; @BeforeEach @@ -53,7 +53,7 @@ void setUp() { ReflectionTestUtils.setField(jwtTokenProvider, "salt", salt); jwtTokenProvider.init(); // PostConstruct 메서드 직접 호출 - testMember = Member.builder() + testApplicant = Applicant.builder() .id(1L) .email("test@example.com") .name("Test User") @@ -63,25 +63,25 @@ void setUp() { .jobFamily(JobFamily.BE) .build(); - CustomUserDetails userDetails = new CustomUserDetails(testMember); + CustomUserDetails userDetails = new CustomUserDetails(testApplicant); authentication = new UsernamePasswordAuthenticationToken(userDetails, "", userDetails.getAuthorities()); } @Test void Access_토큰_생성_테스트() { // when - String token = jwtTokenProvider.createAccessToken(authentication, testMember.getId()); + String token = jwtTokenProvider.createAccessToken(authentication, testApplicant.getId()); // then assertThat(token).isNotNull(); assertThat(jwtTokenProvider.validateToken(token)).isTrue(); - assertThat(jwtTokenProvider.getMemberId(token)).isEqualTo(testMember.getId()); + assertThat(jwtTokenProvider.getApplicantId(token)).isEqualTo(testApplicant.getId()); } @Test void Refresh_토큰_생성_테스트() { // when - String token = jwtTokenProvider.createRefreshToken(authentication, testMember.getId()); + String token = jwtTokenProvider.createRefreshToken(authentication, testApplicant.getId()); // then assertThat(token).isNotNull(); @@ -91,15 +91,15 @@ void setUp() { @Test void 토큰으로부터_Authentication_객체_추출_테스트() { // given - String token = jwtTokenProvider.createAccessToken(authentication, testMember.getId()); + String token = jwtTokenProvider.createAccessToken(authentication, testApplicant.getId()); // when Authentication resultAuth = jwtTokenProvider.getAuthenticationByToken(token); // then assertThat(resultAuth).isNotNull(); - assertThat(resultAuth.getName()).isEqualTo(testMember.getEmail()); - assertThat(((CustomUserDetails) resultAuth.getPrincipal()).getMemberId()).isEqualTo(testMember.getId()); + assertThat(resultAuth.getName()).isEqualTo(testApplicant.getEmail()); + assertThat(((CustomUserDetails) resultAuth.getPrincipal()).getApplicantId()).isEqualTo(testApplicant.getId()); } @Test @@ -137,17 +137,17 @@ void setUp() { @Test void 토큰_재발급_테스트() { // given - String refreshToken = jwtTokenProvider.createRefreshToken(authentication, testMember.getId()); + String refreshToken = jwtTokenProvider.createRefreshToken(authentication, testApplicant.getId()); // when - String newAccessToken = jwtTokenProvider.reissueAccessToken(refreshToken, testMember.getId()); + String newAccessToken = jwtTokenProvider.reissueAccessToken(refreshToken, testApplicant.getId()); // then assertThat(newAccessToken).isNotNull(); assertThat(jwtTokenProvider.validateToken(newAccessToken)).isTrue(); - assertThat(jwtTokenProvider.getMemberId(newAccessToken)).isEqualTo(testMember.getId()); + assertThat(jwtTokenProvider.getApplicantId(newAccessToken)).isEqualTo(testApplicant.getId()); Authentication resultAuth = jwtTokenProvider.getAuthenticationByToken(newAccessToken); - assertThat(resultAuth.getName()).isEqualTo(testMember.getEmail()); + assertThat(resultAuth.getName()).isEqualTo(testApplicant.getEmail()); } @Test @@ -162,7 +162,7 @@ void setUp() { @Test void Authentication이_null일_때_예외_발생_테스트() { // when & then - assertThatThrownBy(() -> jwtTokenProvider.createAccessToken(null, testMember.getId())) + assertThatThrownBy(() -> jwtTokenProvider.createAccessToken(null, testApplicant.getId())) .isInstanceOf(GlobalException.class) .extracting(e -> ((GlobalException) e).getErrorCode().getMessage()) .isEqualTo(AUTHENTICATION_REQUIRED.getMessage()); @@ -171,7 +171,7 @@ void setUp() { @Test void 토큰에_ROLE_접두사가_포함된_역할이_저장되는지_확인() { // when - String token = jwtTokenProvider.createAccessToken(authentication, testMember.getId()); + String token = jwtTokenProvider.createAccessToken(authentication, testApplicant.getId()); // then Authentication resultAuth = jwtTokenProvider.getAuthenticationByToken(token); @@ -200,7 +200,7 @@ void setUp() { void 토큰에서_ROLE_접두사가_있는_역할을_추출할_때_정상적으로_처리되는지_확인() { // given // 테스트를 위해 ROLE_ 접두사가 있는 역할을 가진 사용자로 테스트 멤버 재설정 - testMember = Member.builder() + testApplicant = Applicant.builder() .id(2L) .email("admin@example.com") .name("Admin User") @@ -209,17 +209,17 @@ void setUp() { .role(Role.ADMIN) .build(); - CustomUserDetails userDetails = new CustomUserDetails(testMember); + CustomUserDetails userDetails = new CustomUserDetails(testApplicant); authentication = new UsernamePasswordAuthenticationToken(userDetails, "", userDetails.getAuthorities()); // when - String token = jwtTokenProvider.createAccessToken(authentication, testMember.getId()); + String token = jwtTokenProvider.createAccessToken(authentication, testApplicant.getId()); Authentication resultAuth = jwtTokenProvider.getAuthenticationByToken(token); // then assertThat(resultAuth).isNotNull(); - assertThat(resultAuth.getName()).isEqualTo(testMember.getEmail()); - assertThat(((CustomUserDetails) resultAuth.getPrincipal()).getMemberId()).isEqualTo(testMember.getId()); + assertThat(resultAuth.getName()).isEqualTo(testApplicant.getEmail()); + assertThat(((CustomUserDetails) resultAuth.getPrincipal()).getApplicantId()).isEqualTo(testApplicant.getId()); assertThat(resultAuth.getAuthorities().iterator().next().getAuthority()).isEqualTo("ROLE_ADMIN"); } } diff --git a/src/test/java/org/ject/support/domain/apply/controller/ApplyControllerTest.java b/src/test/java/org/ject/support/domain/apply/controller/ApplyControllerTest.java index 56a6d2d8..d2185aeb 100644 --- a/src/test/java/org/ject/support/domain/apply/controller/ApplyControllerTest.java +++ b/src/test/java/org/ject/support/domain/apply/controller/ApplyControllerTest.java @@ -3,7 +3,7 @@ import com.fasterxml.jackson.databind.ObjectMapper; import org.ject.support.base.UnitTestSupport; import org.ject.support.common.response.ResponseWrapper; -import org.ject.support.common.security.AuthenticatedMemberIdResolver; +import org.ject.support.common.security.AuthenticatedApplicantIdResolver; import org.ject.support.common.security.CustomUserDetails; import org.ject.support.domain.apply.dto.ApplyProfileRequest; import org.ject.support.domain.apply.dto.ApplyStatusResponse; @@ -59,7 +59,7 @@ void setUp() { objectMapper = new ObjectMapper(); mockMvc = MockMvcBuilders.standaloneSetup(applyController) .setControllerAdvice(new ResponseWrapper()) - .setCustomArgumentResolvers(new AuthenticatedMemberIdResolver()) + .setCustomArgumentResolvers(new AuthenticatedApplicantIdResolver()) .build(); } diff --git a/src/test/java/org/ject/support/domain/auth/AuthControllerTest.java b/src/test/java/org/ject/support/domain/auth/AuthControllerTest.java index 8fd2d612..954f5943 100644 --- a/src/test/java/org/ject/support/domain/auth/AuthControllerTest.java +++ b/src/test/java/org/ject/support/domain/auth/AuthControllerTest.java @@ -58,7 +58,7 @@ class AuthControllerTest extends UnitTestSupport { private final String TEST_EMAIL = "test@example.com"; private final String TEST_AUTH_CODE = "123456"; private final String TEST_REFRESH_TOKEN = "test.refresh.token"; - private final Long TEST_MEMBER_ID = 1L; + private final Long TEST_APPLICANT_ID = 1L; @Test void 인증_코드_검증_AUTH_CODE_템플릿_이메일만_반환_성공() { @@ -106,14 +106,14 @@ class AuthControllerTest extends UnitTestSupport { HttpServletResponse mockResponse = mock(HttpServletResponse.class); given(jwtTokenProvider.resolveRefreshToken(mockRequest)).willReturn(TEST_REFRESH_TOKEN); - given(authService.refreshAccessToken(TEST_REFRESH_TOKEN)).willReturn(TEST_MEMBER_ID); + given(authService.refreshAccessToken(TEST_REFRESH_TOKEN)).willReturn(TEST_APPLICANT_ID); // when authController.refreshToken(mockRequest, mockResponse); // then verify(authService).refreshAccessToken(TEST_REFRESH_TOKEN); - verify(customSuccessHandler).onAuthenticationSuccess(mockResponse, TEST_REFRESH_TOKEN, TEST_MEMBER_ID); + verify(customSuccessHandler).onAuthenticationSuccess(mockResponse, TEST_REFRESH_TOKEN, TEST_APPLICANT_ID); } @Test diff --git a/src/test/java/org/ject/support/domain/auth/AuthServiceTest.java b/src/test/java/org/ject/support/domain/auth/AuthServiceTest.java index a2477570..72e6f385 100644 --- a/src/test/java/org/ject/support/domain/auth/AuthServiceTest.java +++ b/src/test/java/org/ject/support/domain/auth/AuthServiceTest.java @@ -5,7 +5,7 @@ import static org.ject.support.domain.auth.exception.AuthErrorCode.EXPIRED_REFRESH_TOKEN; import static org.ject.support.domain.auth.exception.AuthErrorCode.INVALID_REFRESH_TOKEN; import static org.ject.support.domain.auth.exception.AuthErrorCode.INVALID_CREDENTIALS; -import static org.ject.support.domain.member.exception.MemberErrorCode.NOT_FOUND_MEMBER; +import static org.ject.support.domain.applicant.exception.ApplicantErrorCode.NOT_FOUND_APPLICANT; import static org.mockito.ArgumentMatchers.any; import static org.mockito.BDDMockito.given; import static org.mockito.Mockito.verify; @@ -25,10 +25,10 @@ import java.util.Optional; +import org.ject.support.domain.applicant.entity.Applicant; +import org.ject.support.domain.applicant.exception.ApplicantException; +import org.ject.support.domain.applicant.repository.ApplicantRepository; import org.ject.support.domain.member.Role; -import org.ject.support.domain.member.entity.Member; -import org.ject.support.domain.member.exception.MemberException; -import org.ject.support.domain.member.repository.MemberRepository; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; @@ -59,7 +59,7 @@ class AuthServiceTest { private ValueOperations valueOperations; @Mock - private MemberRepository memberRepository; + private ApplicantRepository applicantRepository; @Mock private Authentication authentication; @@ -74,7 +74,7 @@ class AuthServiceTest { private final String TEST_ENCODED_PIN = "123456"; private final String TEST_ACCESS_TOKEN = "test.access.token"; private final String TEST_REFRESH_TOKEN = "test.refresh.token"; - private final Long TEST_MEMBER_ID = 1L; + private final Long TEST_APPLICANT_ID = 1L; @BeforeEach void setUp() { @@ -85,8 +85,8 @@ void setUp() { @DisplayName("PIN 재설정을 위한 이메일 인증 코드 검증 성공 - 액세스 토큰 발급") void verifyEmailByAuthCodeOnly_PinReset_Success() { // given - Member member = Member.builder() - .id(TEST_MEMBER_ID) + Applicant applicant = Applicant.builder() + .id(TEST_APPLICANT_ID) .email(TEST_EMAIL) .pin(TEST_ENCODED_PIN) .status(MemberStatus.ACTIVE) @@ -94,9 +94,9 @@ void verifyEmailByAuthCodeOnly_PinReset_Success() { .build(); given(valueOperations.get(TEST_EMAIL)).willReturn(TEST_AUTH_CODE); - given(memberRepository.findByEmail(TEST_EMAIL)).willReturn(Optional.of(member)); - given(jwtTokenProvider.createAuthenticationByMember(member)).willReturn(authentication); - given(jwtTokenProvider.createAccessToken(authentication, TEST_MEMBER_ID)).willReturn(TEST_ACCESS_TOKEN); + given(applicantRepository.findByEmail(TEST_EMAIL)).willReturn(Optional.of(applicant)); + given(jwtTokenProvider.createAuthenticationByApplicant(applicant)).willReturn(authentication); + given(jwtTokenProvider.createAccessToken(authentication, TEST_APPLICANT_ID)).willReturn(TEST_ACCESS_TOKEN); // when AuthVerificationResult result = authService.verifyAuthCodeByTemplate(TEST_EMAIL, TEST_AUTH_CODE, EmailTemplate.PIN_RESET); @@ -113,14 +113,14 @@ void verifyEmailByAuthCodeOnly_PinReset_Success() { void refreshAccessToken_Success() { // given given(jwtTokenProvider.validateToken(TEST_REFRESH_TOKEN)).willReturn(true); - given(jwtTokenProvider.extractMemberId(TEST_REFRESH_TOKEN)).willReturn(TEST_MEMBER_ID); - given(jwtTokenProvider.reissueAccessToken(TEST_REFRESH_TOKEN, TEST_MEMBER_ID)).willReturn(TEST_ACCESS_TOKEN); + given(jwtTokenProvider.extractApplicantId(TEST_REFRESH_TOKEN)).willReturn(TEST_APPLICANT_ID); + given(jwtTokenProvider.reissueAccessToken(TEST_REFRESH_TOKEN, TEST_APPLICANT_ID)).willReturn(TEST_ACCESS_TOKEN); // when Long result = authService.refreshAccessToken(TEST_REFRESH_TOKEN); // then - assertThat(result).isEqualTo(TEST_MEMBER_ID); + assertThat(result).isEqualTo(TEST_APPLICANT_ID); } @Test @@ -166,19 +166,19 @@ void refreshAccessToken_JwtException_ThrowsException() { @DisplayName("PIN 로그인 성공") void loginWithPin_Success() { // given - Member member = Member.builder() - .id(TEST_MEMBER_ID) + Applicant applicant = Applicant.builder() + .id(TEST_APPLICANT_ID) .email(TEST_EMAIL) .pin(TEST_ENCODED_PIN) .role(Role.SEMESTER) .status(MemberStatus.ACTIVE) .build(); - given(memberRepository.findByEmail(TEST_EMAIL)).willReturn(Optional.of(member)); + given(applicantRepository.findByEmail(TEST_EMAIL)).willReturn(Optional.of(applicant)); given(passwordEncoder.matches(TEST_PIN, TEST_ENCODED_PIN)).willReturn(true); - given(jwtTokenProvider.createAuthenticationByMember(member)).willReturn(authentication); - given(jwtTokenProvider.createAccessToken(authentication, TEST_MEMBER_ID)).willReturn(TEST_ACCESS_TOKEN); - given(jwtTokenProvider.createRefreshToken(authentication, member.getId())).willReturn(TEST_REFRESH_TOKEN); + given(jwtTokenProvider.createAuthenticationByApplicant(applicant)).willReturn(authentication); + given(jwtTokenProvider.createAccessToken(authentication, TEST_APPLICANT_ID)).willReturn(TEST_ACCESS_TOKEN); + given(jwtTokenProvider.createRefreshToken(authentication, applicant.getId())).willReturn(TEST_REFRESH_TOKEN); // when Authentication result = authService.loginWithPin(TEST_EMAIL, TEST_PIN); @@ -191,28 +191,28 @@ void loginWithPin_Success() { @DisplayName("PIN 로그인 실패 - 회원 없음") void loginWithPin_MemberNotFound_ThrowsException() { // given - given(memberRepository.findByEmail(TEST_EMAIL)).willReturn(Optional.empty()); + given(applicantRepository.findByEmail(TEST_EMAIL)).willReturn(Optional.empty()); // when & then assertThatThrownBy(() -> authService.loginWithPin(TEST_EMAIL, TEST_PIN)) - .isInstanceOf(MemberException.class) - .extracting(e -> ((MemberException) e).getErrorCode()) - .isEqualTo(NOT_FOUND_MEMBER); + .isInstanceOf(ApplicantException.class) + .extracting(e -> ((ApplicantException) e).getErrorCode()) + .isEqualTo(NOT_FOUND_APPLICANT); } @Test @DisplayName("PIN 로그인 실패 - 잘못된 PIN") void loginWithPin_InvalidPin_ThrowsException() { // given - Member member = Member.builder() - .id(TEST_MEMBER_ID) + Applicant applicant = Applicant.builder() + .id(TEST_APPLICANT_ID) .email(TEST_EMAIL) .pin(TEST_ENCODED_PIN) .role(Role.SEMESTER) .status(MemberStatus.ACTIVE) .build(); - given(memberRepository.findByEmail(TEST_EMAIL)).willReturn(Optional.of(member)); + given(applicantRepository.findByEmail(TEST_EMAIL)).willReturn(Optional.of(applicant)); given(passwordEncoder.matches(TEST_PIN, TEST_ENCODED_PIN)).willReturn(false); // when & then @@ -226,7 +226,7 @@ void loginWithPin_InvalidPin_ThrowsException() { @DisplayName("회원 존재 여부 확인 - 회원 존재") void isExistMember_MemberExists_ReturnsTrue() { // given - given(memberRepository.existsByEmail(TEST_EMAIL)).willReturn(true); + given(applicantRepository.existsByEmail(TEST_EMAIL)).willReturn(true); // when boolean result = authService.isExistMember(TEST_EMAIL); @@ -239,7 +239,7 @@ void isExistMember_MemberExists_ReturnsTrue() { @DisplayName("회원 존재 여부 확인 - 회원 없음") void isExistMember_MemberNotExists_ReturnsFalse() { // given - given(memberRepository.existsByEmail(TEST_EMAIL)).willReturn(false); + given(applicantRepository.existsByEmail(TEST_EMAIL)).willReturn(false); // when boolean result = authService.isExistMember(TEST_EMAIL); @@ -271,16 +271,16 @@ void verifyAuthCodeByTemplate_WithCertificateTemplate_ReturnsEmailResult() { @DisplayName("인증번호 검증 - PIN_RESET 템플릿") void verifyAuthCodeByTemplate_WithPinResetTemplate_ReturnsAuthenticationResult() { // given - Member member = Member.builder() + Applicant applicant = Applicant.builder() .email(TEST_EMAIL) .name("테스트") .status(MemberStatus.ACTIVE) .build(); Authentication mockAuthentication = mock(Authentication.class); - given(memberRepository.findByEmail(TEST_EMAIL)).willReturn(Optional.of(member)); + given(applicantRepository.findByEmail(TEST_EMAIL)).willReturn(Optional.of(applicant)); given(redisTemplate.opsForValue().get(TEST_EMAIL)).willReturn(TEST_AUTH_CODE); - given(jwtTokenProvider.createAuthenticationByMember(any(Member.class))).willReturn(mockAuthentication); + given(jwtTokenProvider.createAuthenticationByApplicant(any(Applicant.class))).willReturn(mockAuthentication); // when AuthVerificationResult result = authService.verifyAuthCodeByTemplate(TEST_EMAIL, TEST_AUTH_CODE, EmailTemplate.PIN_RESET); diff --git a/src/test/java/org/ject/support/domain/member/controller/MemberControllerTest.java b/src/test/java/org/ject/support/domain/member/controller/MemberControllerTest.java deleted file mode 100644 index a30403e5..00000000 --- a/src/test/java/org/ject/support/domain/member/controller/MemberControllerTest.java +++ /dev/null @@ -1,288 +0,0 @@ -package org.ject.support.domain.member.controller; - -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.anyString; -import static org.mockito.ArgumentMatchers.eq; -import static org.mockito.BDDMockito.given; -import static org.mockito.Mockito.doNothing; -import static org.mockito.Mockito.lenient; -import static org.mockito.Mockito.verify; -import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post; -import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.put; -import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; -import static org.springframework.test.web.servlet.setup.MockMvcBuilders.standaloneSetup; - -import com.fasterxml.jackson.databind.ObjectMapper; -import jakarta.servlet.http.HttpServletRequest; -import jakarta.servlet.http.HttpServletResponse; -import org.ject.support.common.security.CustomSuccessHandler; -import org.ject.support.common.security.CustomUserDetails; -import org.ject.support.common.security.jwt.JwtTokenProvider; -import org.ject.support.domain.member.Role; -import org.ject.support.domain.member.dto.MemberDto.InitialProfileRequest; -import org.ject.support.domain.member.dto.MemberDto.RegisterRequest; -import org.ject.support.domain.member.dto.MemberDto.UpdatePinRequest; -import org.ject.support.domain.member.service.MemberService; -import org.ject.support.testconfig.ApplicationPeriodTest; -import org.ject.support.testconfig.AuthenticatedUser; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.DisplayName; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.ExtendWith; -import org.mockito.InjectMocks; -import org.mockito.Mock; -import org.mockito.junit.jupiter.MockitoExtension; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; -import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.http.MediaType; -import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; -import org.springframework.security.core.Authentication; -import org.springframework.security.core.context.SecurityContextHolder; -import org.springframework.test.context.TestPropertySource; -import org.springframework.test.web.servlet.MockMvc; - -@ExtendWith(MockitoExtension.class) -class MemberControllerTest { - - @InjectMocks - private MemberController memberController; - - @Mock - private MemberService memberService; - - @Mock - private JwtTokenProvider jwtTokenProvider; - - @Mock - private CustomSuccessHandler customSuccessHandler; - - private MockMvc mockMvc; - private ObjectMapper objectMapper; - - private final String TEST_NAME = "홍길동"; - private final String TEST_EMAIL = "test@example.com"; - private final String TEST_PHONE_NUMBER = "01012345678"; - private final String TEST_PIN = "123456"; - private final String TEST_VERIFICATION_TOKEN = "test.verification.token"; - - @BeforeEach - void setUp() { - objectMapper = new ObjectMapper(); - mockMvc = standaloneSetup(memberController).build(); - } - - @Test - void 회원_등록_성공() throws Exception { - // given - RegisterRequest request = new RegisterRequest(TEST_PIN); - Authentication mockAuthentication = new UsernamePasswordAuthenticationToken( - new CustomUserDetails(TEST_EMAIL, 1L, Role.APPLY), "", null); - - given(jwtTokenProvider.resolveVerificationToken(any())).willReturn(TEST_VERIFICATION_TOKEN); - given(jwtTokenProvider.extractEmailFromVerificationToken(TEST_VERIFICATION_TOKEN)).willReturn(TEST_EMAIL); - given(memberService.registerTempMember(any(RegisterRequest.class), anyString())).willReturn(mockAuthentication); - - // customSuccessHandler.onAuthenticationSuccess 메소드 호출 모킹 - doNothing().when(customSuccessHandler).onAuthenticationSuccess(any(HttpServletRequest.class), any( - HttpServletResponse.class), any(Authentication.class)); - - // when & then - mockMvc.perform(post("/members/apply") - .cookie(new jakarta.servlet.http.Cookie("verificationToken", TEST_VERIFICATION_TOKEN)) - .contentType(MediaType.APPLICATION_JSON) - .content(objectMapper.writeValueAsString(request))) - .andExpect(status().isOk()); - - verify(jwtTokenProvider).resolveVerificationToken(any()); - verify(jwtTokenProvider).extractEmailFromVerificationToken(TEST_VERIFICATION_TOKEN); - verify(memberService).registerTempMember(any(RegisterRequest.class), eq(TEST_EMAIL)); - verify(customSuccessHandler).onAuthenticationSuccess(any(HttpServletRequest.class), - any(HttpServletResponse.class), eq(mockAuthentication)); - } - - @Test - void 임시회원_최초_프로필_등록_성공() throws Exception { - // given - InitialProfileRequest request = new InitialProfileRequest(TEST_NAME, TEST_PHONE_NUMBER); - Long memberId = 1L; - - // lenient 설정을 사용하여 엄격한 스텔빙 검사를 해제 - lenient().doNothing().when(memberService).registerInitialProfile(any(), eq(memberId)); - - // CustomUserDetails를 사용하여 인증 정보 설정 (ROLE_APPLY 권한) - CustomUserDetails userDetails = new CustomUserDetails(TEST_EMAIL, memberId, Role.APPLY); - Authentication auth = new UsernamePasswordAuthenticationToken(userDetails, "", userDetails.getAuthorities()); - SecurityContextHolder.getContext().setAuthentication(auth); - - // when & then - mockMvc.perform(put("/members/profile/initial") - .contentType(MediaType.APPLICATION_JSON) - .content(objectMapper.writeValueAsString(request))) - .andExpect(status().isOk()); - - // verify를 사용하지 않고 스텔빙만 확인 - // 테스트 후 인증 정보 초기화 - SecurityContextHolder.clearContext(); - } - - @Test - void 핀번호_재설정_성공() throws Exception { - // given - UpdatePinRequest request = new UpdatePinRequest("654321"); // 새로운 PIN 번호 - Long memberId = 1L; - - // lenient 설정을 사용하여 엄격한 스텔빙 검사를 해제 - lenient().doNothing().when(memberService).updatePin(any(UpdatePinRequest.class), eq(memberId)); - - // CustomUserDetails를 사용하여 인증 정보 설정 (ROLE_APPLY 권한) - CustomUserDetails userDetails = new CustomUserDetails(TEST_EMAIL, memberId, Role.APPLY); - Authentication auth = new UsernamePasswordAuthenticationToken(userDetails, "", userDetails.getAuthorities()); - SecurityContextHolder.getContext().setAuthentication(auth); - - // when & then - mockMvc.perform(put("/members/pin") - .contentType(MediaType.APPLICATION_JSON) - .content(objectMapper.writeValueAsString(request))) - .andExpect(status().isOk()); - - // verify를 사용하지 않고 스텔빙만 확인 - // 테스트 후 인증 정보 초기화 - SecurityContextHolder.clearContext(); - } - - @Test - void 핀번호_재설정_실패_유효하지_않은_PIN_번호_형식() throws Exception { - // given - // 유효하지 않은 PIN 번호 (6자리가 아님) - String invalidPin = "12345"; - UpdatePinRequest request = new UpdatePinRequest(invalidPin); - Long memberId = 1L; - - // CustomUserDetails를 사용하여 인증 정보 설정 (ROLE_APPLY 권한) - CustomUserDetails userDetails = new CustomUserDetails(TEST_EMAIL, memberId, Role.APPLY); - Authentication auth = new UsernamePasswordAuthenticationToken(userDetails, "", userDetails.getAuthorities()); - SecurityContextHolder.getContext().setAuthentication(auth); - - // when & then - mockMvc.perform(put("/members/pin") - .contentType(MediaType.APPLICATION_JSON) - .content(objectMapper.writeValueAsString(request))) - .andExpect(status().isBadRequest()); - - // 테스트 후 인증 정보 초기화 - SecurityContextHolder.clearContext(); - } - - @Test - void 임시회원_최초_프로필_등록_여부_확인_성공_등록된_경우() throws Exception { - // given - Long memberId = 1L; - - // 모킹 설정을 lenient로 변경하여 엄격한 스텁 검사를 비활성화 - lenient().when(memberService.checkIsInitialed(any())).thenReturn(true); - - // CustomUserDetails를 사용하여 인증 정보 설정 (ROLE_APPLY 권한) - CustomUserDetails userDetails = new CustomUserDetails(TEST_EMAIL, memberId, Role.APPLY); - Authentication auth = new UsernamePasswordAuthenticationToken(userDetails, "", userDetails.getAuthorities()); - SecurityContextHolder.getContext().setAuthentication(auth); - - // when & then - mockMvc.perform(org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get( - "/members/profile/initial/status") - .contentType(MediaType.APPLICATION_JSON) - .principal(auth)) - .andExpect(status().isOk()) - .andExpect(org.springframework.test.web.servlet.result.MockMvcResultMatchers.content().string("true")); - - // 테스트 후 인증 정보 초기화 - SecurityContextHolder.clearContext(); - } - - @Test - void 임시회원_최초_프로필_등록_여부_확인_성공_미등록된_경우() throws Exception { - // given - Long memberId = 1L; - - // 모킹 설정을 lenient로 변경하여 엄격한 스텁 검사를 비활성화 - lenient().when(memberService.checkIsInitialed(any())).thenReturn(false); - - // CustomUserDetails를 사용하여 인증 정보 설정 (ROLE_APPLY 권한) - CustomUserDetails userDetails = new CustomUserDetails(TEST_EMAIL, memberId, Role.APPLY); - Authentication auth = new UsernamePasswordAuthenticationToken(userDetails, "", userDetails.getAuthorities()); - SecurityContextHolder.getContext().setAuthentication(auth); - - // when & then - mockMvc.perform(org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get( - "/members/profile/initial/status") - .contentType(MediaType.APPLICATION_JSON) - .principal(auth)) - .andExpect(status().isOk()) - .andExpect(org.springframework.test.web.servlet.result.MockMvcResultMatchers.content().string("false")); - - // 테스트 후 인증 정보 초기화 - SecurityContextHolder.clearContext(); - } - - - @SpringBootTest - @AutoConfigureMockMvc - @TestPropertySource(properties = {"spring.data.redis.repositories.enabled=false", "server.port=0"}) - class MemberControllerIntegrationTest extends ApplicationPeriodTest { - - private final String TEST_PIN = "123456"; - private final String TEST_VERIFICATION_TOKEN = "test.verification.token"; - private final String TEST_NAME = "홍길동"; - private final String TEST_PHONE_NUMBER = "01012345678"; - @Autowired - private MockMvc mockMvc; - @Autowired - private ObjectMapper objectMapper; - - @Test - @DisplayName("회원 등록 API 통합 테스트") - void registerMember_Integration() throws Exception { - // given - RegisterRequest request = new RegisterRequest(TEST_PIN); - - // 실제 서비스를 사용하므로 모킹하지 않음 - // 대신 응답 상태만 확인 - - // when & then - mockMvc.perform(post("/members") - .cookie(new jakarta.servlet.http.Cookie("verificationToken", TEST_VERIFICATION_TOKEN)) - .contentType(MediaType.APPLICATION_JSON) - .content(objectMapper.writeValueAsString(request))) - .andExpect(status().isOk()); - // 응답 본문이 없으므로 상태 코드만 확인 - } - - @Test - @DisplayName("임시회원 최초 프로필 등록 API 통합 테스트") - @AuthenticatedUser(memberId = 1L) - void registerInitialProfile_Integration() throws Exception { - // given - InitialProfileRequest request = new InitialProfileRequest(TEST_NAME, TEST_PHONE_NUMBER); - - // when & then - mockMvc.perform(put("/members/profile/initial") - .contentType(MediaType.APPLICATION_JSON) - .content(objectMapper.writeValueAsString(request))) - .andExpect(status().isOk()); - } - - @Test - @DisplayName("핀번호 재설정 API 통합 테스트") - @AuthenticatedUser(memberId = 1L) - void resetPin_Integration() throws Exception { - // given - UpdatePinRequest request = new UpdatePinRequest("654321"); // 새로운 PIN 번호 - - // when & then - mockMvc.perform(put("/members/pin") - .contentType(MediaType.APPLICATION_JSON) - .content(objectMapper.writeValueAsString(request))) - .andExpect(status().isOk()); - } - } -} \ No newline at end of file diff --git a/src/test/java/org/ject/support/domain/member/service/MemberServiceTest.java b/src/test/java/org/ject/support/domain/member/service/MemberServiceTest.java deleted file mode 100644 index 8b1be908..00000000 --- a/src/test/java/org/ject/support/domain/member/service/MemberServiceTest.java +++ /dev/null @@ -1,247 +0,0 @@ -package org.ject.support.domain.member.service; - -import org.ject.support.base.UnitTestSupport; -import org.ject.support.common.security.jwt.JwtTokenProvider; -import org.ject.support.domain.member.CareerDetails; -import org.ject.support.domain.member.ExperiencePeriod; -import org.ject.support.domain.member.MemberStatus; -import org.ject.support.domain.member.Region; -import org.ject.support.domain.member.dto.MemberDto.InitialProfileRequest; -import org.ject.support.domain.member.dto.MemberDto.RegisterRequest; -import org.ject.support.domain.member.dto.MemberDto.UpdatePinRequest; -import org.ject.support.domain.member.dto.MemberProfileResponse; -import org.ject.support.domain.member.entity.Member; -import org.ject.support.domain.member.exception.MemberErrorCode; -import org.ject.support.domain.member.exception.MemberException; -import org.ject.support.domain.member.repository.MemberRepository; -import org.ject.support.domain.recruit.domain.Semester; -import org.ject.support.domain.recruit.repository.SemesterRepository; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.mockito.InjectMocks; -import org.mockito.Mock; -import org.springframework.security.core.Authentication; -import org.springframework.security.crypto.password.PasswordEncoder; - -import java.util.List; -import java.util.Optional; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.assertThatThrownBy; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.BDDMockito.given; -import static org.mockito.Mockito.verify; - -class MemberServiceTest extends UnitTestSupport { - - @InjectMocks - private MemberService memberService; - - @Mock - private MemberRepository memberRepository; - - @Mock - private JwtTokenProvider jwtTokenProvider; - - @Mock - private PasswordEncoder passwordEncoder; - - @Mock - private Authentication authentication; - - @Mock - private SemesterRepository semesterRepository; - - private final String TEST_NAME = "홍길동"; - private final String TEST_EMAIL = "test@example.com"; - private final String TEST_PHONE_NUMBER = "01012345678"; - private final String TEST_PIN = "123456"; - private final String TEST_ENCODED_PIN = "encoded_pin"; - - @BeforeEach - void setUp() { - // 기본 설정 - } - - @Test - void 임시_회원_등록_성공() { - // given - RegisterRequest request = new RegisterRequest(TEST_PIN); - Member member = Member.builder() - .id(1L) - .email(TEST_EMAIL) - .pin(TEST_ENCODED_PIN) - .status(MemberStatus.ACTIVE) - .build(); - Semester semester = Semester.builder() - .id(1L) - .name("1기") - .isRecruiting(true) - .build(); - - given(memberRepository.findByEmail(TEST_EMAIL)).willReturn(Optional.empty()); - given(passwordEncoder.encode(TEST_PIN)).willReturn(TEST_ENCODED_PIN); - given(memberRepository.save(any(Member.class))).willReturn(member); - given(jwtTokenProvider.createAuthenticationByMember(any(Member.class))).willReturn(authentication); - given(semesterRepository.findRecruitingSemester()).willReturn(Optional.of(semester)); - - // when - Authentication result = memberService.registerTempMember(request, TEST_EMAIL); - - // then - assertThat(result).isEqualTo(authentication); - verify(memberRepository).save(any(Member.class)); - verify(passwordEncoder).encode(TEST_PIN); - verify(jwtTokenProvider).createAuthenticationByMember(any(Member.class)); - } - - @Test - void 이미_존재하는_회원인_경우_예외_발생() { - // given - RegisterRequest request = new RegisterRequest(TEST_PIN); - Member existingMember = Member.builder() - .email(TEST_EMAIL) - .pin(TEST_ENCODED_PIN) - .status(MemberStatus.ACTIVE) - .build(); - - given(memberRepository.findByEmail(TEST_EMAIL)).willReturn(Optional.of(existingMember)); - - // when & then - assertThatThrownBy(() -> memberService.registerTempMember(request, TEST_EMAIL)) - .isInstanceOf(MemberException.class) - .extracting(e -> ((MemberException) e).getErrorCode()) - .isEqualTo(MemberErrorCode.ALREADY_EXIST_MEMBER); - } - - @Test - void 회원_정보_업데이트_성공() { - // given - Long memberId = 1L; - InitialProfileRequest request = new InitialProfileRequest(TEST_NAME, TEST_PHONE_NUMBER); - Member member = Member.builder() - .id(memberId) - .email(TEST_EMAIL) - .pin(TEST_ENCODED_PIN) - .status(MemberStatus.ACTIVE) - .build(); - - given(memberRepository.findById(memberId)).willReturn(Optional.of(member)); - - // when - memberService.registerInitialProfile(request, memberId); - - // then - assertThat(member.getName()).isEqualTo(TEST_NAME); - assertThat(member.getPhoneNumber()).isEqualTo(TEST_PHONE_NUMBER); - verify(memberRepository).findById(memberId); - } - - @Test - void 존재하지_않는_회원_정보_업데이트_시_예외_발생() { - // given - Long memberId = 1L; - InitialProfileRequest request = new InitialProfileRequest(TEST_NAME, TEST_PHONE_NUMBER); - - given(memberRepository.findById(memberId)).willReturn(Optional.empty()); - - // when & then - assertThatThrownBy(() -> memberService.registerInitialProfile(request, memberId)) - .isInstanceOf(MemberException.class) - .extracting(e -> ((MemberException) e).getErrorCode()) - .isEqualTo(MemberErrorCode.NOT_FOUND_MEMBER); - } - - @Test - void 핀번호_재설정_성공() { - // given - Long memberId = 1L; - String newPin = "654321"; - UpdatePinRequest request = new UpdatePinRequest(newPin); - - Member member = Member.builder() - .id(memberId) - .email(TEST_EMAIL) - .pin(TEST_ENCODED_PIN) - .status(MemberStatus.ACTIVE) - .build(); - - String newEncodedPin = "new_encoded_pin"; - - given(memberRepository.findById(memberId)).willReturn(Optional.of(member)); - given(passwordEncoder.encode(newPin)).willReturn(newEncodedPin); - - // when - memberService.updatePin(request, memberId); - - // then - assertThat(member.getPin()).isEqualTo(newEncodedPin); - verify(memberRepository).findById(memberId); - verify(passwordEncoder).encode(newPin); - } - - @Test - void 핀번호_재설정_실패_존재하지_않는_회원() { - // given - Long memberId = 1L; - UpdatePinRequest request = new UpdatePinRequest("654321"); - - given(memberRepository.findById(memberId)).willReturn(Optional.empty()); - - // when & then - assertThatThrownBy(() -> memberService.updatePin(request, memberId)) - .isInstanceOf(MemberException.class) - .extracting(e -> ((MemberException) e).getErrorCode()) - .isEqualTo(MemberErrorCode.NOT_FOUND_MEMBER); - } - - @Test - void 존재하지않는_회원ID로_회원_프로필_정보를_조회할_경우_NOT_FOUND_MEMBER_예외_발생() { - // given - Long memberId = 1L; - given(memberRepository.findById(memberId)) - .willReturn(Optional.empty()); - - // when & then - assertThatThrownBy(() -> memberService.getMemberProfile(memberId)) - .isInstanceOf(MemberException.class) - .extracting(e -> ((MemberException) e).getErrorCode()) - .isEqualTo(MemberErrorCode.NOT_FOUND_MEMBER); - } - - @Test - void 회원ID로_회원_프로필_정보를_조회할_경우_회원정보를_반환한다() { - // given - Long memberId = 1L; - String name = "프로필"; - String phoneNumber = "01012345678"; - CareerDetails careerDetails = CareerDetails.EMPLOYEE; - Region region = Region.BUSAN; - ExperiencePeriod experiencePeriod = ExperiencePeriod.FIVE_PLUS; - List interestedDomains = List.of("BACKEND", "FRONTEND"); - - Member member = Member.builder() - .id(memberId) - .name(name) - .phoneNumber(phoneNumber) - .careerDetails(careerDetails) - .region(region) - .experiencePeriod(experiencePeriod) - .interestedDomains(interestedDomains) - .build(); - - given(memberRepository.findById(memberId)) - .willReturn(Optional.of(member)); - - // when - MemberProfileResponse result = memberService.getMemberProfile(memberId); - - // then - assertThat(result.id()).isEqualTo(memberId); - assertThat(result.name()).isEqualTo(name); - assertThat(result.careerDetails()).isEqualTo(careerDetails); - assertThat(result.region()).isEqualTo(region); - assertThat(result.experiencePeriod()).isEqualTo(experiencePeriod); - assertThat(result.interestedDomains()).isEqualTo(interestedDomains); - } -} diff --git a/src/test/java/org/ject/support/testconfig/AuthenticatedUser.java b/src/test/java/org/ject/support/testconfig/AuthenticatedUser.java index 0f0861ae..ef0f91a4 100644 --- a/src/test/java/org/ject/support/testconfig/AuthenticatedUser.java +++ b/src/test/java/org/ject/support/testconfig/AuthenticatedUser.java @@ -7,6 +7,7 @@ @Retention(RetentionPolicy.RUNTIME) @WithSecurityContext(factory = WithAuthenticatedUserSecurityContextFactory.class) public @interface AuthenticatedUser { + long applicantId() default -1L; long memberId() default 1L; String email() default "test1@gmail.com"; boolean isAdmin() default false; diff --git a/src/test/java/org/ject/support/testconfig/WithAuthenticatedUserSecurityContextFactory.java b/src/test/java/org/ject/support/testconfig/WithAuthenticatedUserSecurityContextFactory.java index 4858b33a..ce221a9e 100644 --- a/src/test/java/org/ject/support/testconfig/WithAuthenticatedUserSecurityContextFactory.java +++ b/src/test/java/org/ject/support/testconfig/WithAuthenticatedUserSecurityContextFactory.java @@ -14,10 +14,10 @@ public SecurityContext createSecurityContext(AuthenticatedUser annotation) { SecurityContext context = SecurityContextHolder.createEmptyContext(); String email = annotation.email(); boolean isAdmin = annotation.isAdmin(); - long memberId = annotation.memberId(); + long applicantId = annotation.applicantId() >= 0 ? annotation.applicantId() : annotation.memberId(); Role role = isAdmin ? Role.ADMIN : Role.SEMESTER; - CustomUserDetails userDetails = new CustomUserDetails(email, memberId, role); + CustomUserDetails userDetails = new CustomUserDetails(email, applicantId, role); Authentication auth = new UsernamePasswordAuthenticationToken(userDetails, "", userDetails.getAuthorities()); context.setAuthentication(auth); From dc61ff0aa6abde337023bd0560e0fc2bf5cf894d Mon Sep 17 00:00:00 2001 From: Kang Tae Seong <69503955+kangkings@users.noreply.github.com> Date: Wed, 10 Jun 2026 10:19:10 +0900 Subject: [PATCH 3/4] =?UTF-8?q?[refactor]=20=EC=A7=80=EC=9B=90=EC=84=9C=20?= =?UTF-8?q?=EB=8F=84=EB=A9=94=EC=9D=B8=20=EC=86=8C=EC=9C=A0=EA=B6=8C?= =?UTF-8?q?=EC=9D=84=20applicant=EB=A1=9C=20=EC=A0=84=ED=99=98=20(#573)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * [refactor]: 지원서 소유자를 applicant로 전환 * [db]: 지원서 외래키를 applicant로 전환 * [test]: 지원서 테스트를 applicant 기준으로 수정 * [test]: applicant 계정 조회 쿼리 테스트 이관 * [test]: applicant repository 테스트 네이밍 정리 --- .../apply/dto/AdminApplyDetailResponse.java | 18 +- .../admin/apply/dto/AdminApplyResponse.java | 11 +- .../AdminApplyQueryRepositoryImpl.java | 16 +- .../repository/AdminApplyRepository.java | 12 +- .../apply/service/AdminApplyService.java | 14 +- .../admin/apply/service/ApplyPassService.java | 10 +- .../ApplicantQueryRepositoryImpl.java | 2 +- .../domain/apply/controller/ApplyApiSpec.java | 12 +- .../apply/controller/ApplyController.java | 24 +-- .../support/domain/apply/domain/Apply.java | 10 +- .../repository/ApplicationFormRepository.java | 6 +- .../apply/repository/ApplyRepository.java | 28 +-- .../domain/apply/service/ApplyService.java | 68 +++---- .../domain/apply/service/ApplyUsecase.java | 12 +- .../apply/service/RemindApplyService.java | 8 +- .../member/dto/MemberAccountProjection.java | 13 -- .../repository/MemberQueryRepository.java | 10 - .../repository/MemberQueryRepositoryImpl.java | 76 -------- ...V32__convert_apply_member_to_applicant.sql | 15 ++ .../AdminApplyQueryRepositoryTest.java | 134 ++++++------- .../apply/service/AdminApplyServiceTest.java | 54 +++--- .../apply/service/ApplyPassServiceTest.java | 34 ++-- .../repository/ApplicantRepositoryTest.java | 180 +++++++++++++++++- .../domain/apply/domain/ApplyTest.java | 10 +- .../apply/repository/ApplyRepositoryTest.java | 62 +++--- .../apply/service/ApplyServiceTest.java | 158 +++++++-------- .../MemberAccountQueryRepositoryTest.java | 115 ----------- .../repository/MemberQueryRepositoryTest.java | 59 ------ .../ApplicationFormRepositoryTest.java | 14 +- 29 files changed, 553 insertions(+), 632 deletions(-) delete mode 100644 src/main/java/org/ject/support/domain/member/dto/MemberAccountProjection.java create mode 100644 src/main/resources/db/migration/V32__convert_apply_member_to_applicant.sql delete mode 100644 src/test/java/org/ject/support/domain/member/repository/MemberAccountQueryRepositoryTest.java diff --git a/src/main/java/org/ject/support/admin/apply/dto/AdminApplyDetailResponse.java b/src/main/java/org/ject/support/admin/apply/dto/AdminApplyDetailResponse.java index dac8067d..26b4b9a7 100644 --- a/src/main/java/org/ject/support/admin/apply/dto/AdminApplyDetailResponse.java +++ b/src/main/java/org/ject/support/admin/apply/dto/AdminApplyDetailResponse.java @@ -22,19 +22,19 @@ public record AdminApplyDetailResponse( List interestedDomains ) { public static AdminApplyDetailResponse from(Apply apply) { - var member = apply.getMember(); + var applicant = apply.getApplicant(); return new AdminApplyDetailResponse( apply.getId(), - member.getName(), - member.getPhoneNumber(), - member.getEmail(), - member.getJobFamily(), - Optional.ofNullable(member.getCareerDetails()).map(CareerDetails::getDescription).orElse(""), + applicant.getName(), + applicant.getPhoneNumber(), + applicant.getEmail(), + applicant.getJobFamily(), + Optional.ofNullable(applicant.getCareerDetails()).map(CareerDetails::getDescription).orElse(""), apply.getRecruit().getRecruitType().name(), apply.getNote(), - Optional.ofNullable(member.getRegion()).map(Region::getDescription).orElse(""), - Optional.ofNullable(member.getExperiencePeriod()).map(ExperiencePeriod::getDescription).orElse(""), - Optional.ofNullable(member.getInterestedDomains()).orElse(List.of()) + Optional.ofNullable(applicant.getRegion()).map(Region::getDescription).orElse(""), + Optional.ofNullable(applicant.getExperiencePeriod()).map(ExperiencePeriod::getDescription).orElse(""), + Optional.ofNullable(applicant.getInterestedDomains()).orElse(List.of()) ); } } diff --git a/src/main/java/org/ject/support/admin/apply/dto/AdminApplyResponse.java b/src/main/java/org/ject/support/admin/apply/dto/AdminApplyResponse.java index 094a0f8a..5d3812f2 100644 --- a/src/main/java/org/ject/support/admin/apply/dto/AdminApplyResponse.java +++ b/src/main/java/org/ject/support/admin/apply/dto/AdminApplyResponse.java @@ -16,13 +16,14 @@ public record AdminApplyResponse( String note ) { public static AdminApplyResponse from(Apply apply) { + var applicant = apply.getApplicant(); return new AdminApplyResponse( apply.getId(), - apply.getMember().getName(), - apply.getMember().getPhoneNumber(), - apply.getMember().getEmail(), - apply.getMember().getJobFamily(), - Optional.ofNullable(apply.getMember().getCareerDetails()).map(CareerDetails::getDescription).orElse(""), + applicant.getName(), + applicant.getPhoneNumber(), + applicant.getEmail(), + applicant.getJobFamily(), + Optional.ofNullable(applicant.getCareerDetails()).map(CareerDetails::getDescription).orElse(""), apply.getRecruit().getRecruitType().name(), apply.getNote() ); diff --git a/src/main/java/org/ject/support/admin/apply/repository/AdminApplyQueryRepositoryImpl.java b/src/main/java/org/ject/support/admin/apply/repository/AdminApplyQueryRepositoryImpl.java index 2797f8d5..61e53adc 100644 --- a/src/main/java/org/ject/support/admin/apply/repository/AdminApplyQueryRepositoryImpl.java +++ b/src/main/java/org/ject/support/admin/apply/repository/AdminApplyQueryRepositoryImpl.java @@ -3,7 +3,7 @@ import static org.ject.support.domain.apply.domain.QApplicationForm.applicationForm; import static org.ject.support.domain.apply.domain.QApply.apply; import static org.ject.support.domain.apply.domain.QPortfolio.portfolio; -import static org.ject.support.domain.member.entity.QMember.member; +import static org.ject.support.domain.applicant.entity.QApplicant.applicant; import static org.ject.support.domain.recruit.domain.QRecruit.recruit; import com.querydsl.core.types.dsl.BooleanExpression; @@ -35,12 +35,12 @@ public Page findApplies(final AdminApplySearchCondition condition, List content = queryFactory .selectFrom(apply) .distinct() - .join(apply.member, member).fetchJoin() + .join(apply.applicant, applicant).fetchJoin() .join(apply.applicationForm, applicationForm).fetchJoin() .join(apply.recruit, recruit).fetchJoin() .leftJoin(apply.applicationForm.portfolios, portfolio).fetchJoin() .where( - apply.member.isDeleted.eq(false), + apply.applicant.isDeleted.eq(false), eqJobFamily(condition.jobFamily()), eqApplyStatus(condition.applyStatus()), eqSemesterId(condition.semesterId()), @@ -56,10 +56,10 @@ public Page findApplies(final AdminApplySearchCondition condition, Long total = queryFactory .select(apply.count()) .from(apply) - .join(apply.member, member) + .join(apply.applicant, applicant) .join(apply.recruit, recruit) .where( - apply.member.isDeleted.eq(false), + apply.applicant.isDeleted.eq(false), eqJobFamily(condition.jobFamily()), eqApplyStatus(condition.applyStatus()), eqSemesterId(condition.semesterId()), @@ -76,13 +76,13 @@ public Optional findApplyByIdByStatus(final Long applyId, final ApplyStat Apply result = queryFactory .selectFrom(apply) .distinct() - .join(apply.member, member).fetchJoin() + .join(apply.applicant, applicant).fetchJoin() .join(apply.applicationForm, applicationForm).fetchJoin() .join(apply.recruit, recruit).fetchJoin() .leftJoin(apply.applicationForm.portfolios, portfolio).fetchJoin() .where( apply.id.eq(applyId), - apply.member.isDeleted.eq(false), + apply.applicant.isDeleted.eq(false), eqApplyStatus(status) ) .fetchOne(); @@ -92,7 +92,7 @@ public Optional findApplyByIdByStatus(final Long applyId, final ApplyStat private BooleanExpression eqJobFamily(final JobFamily jobFamily) { return Optional.ofNullable(jobFamily) - .map(apply.member.jobFamily::eq) + .map(apply.applicant.jobFamily::eq) .orElse(null); } diff --git a/src/main/java/org/ject/support/admin/apply/repository/AdminApplyRepository.java b/src/main/java/org/ject/support/admin/apply/repository/AdminApplyRepository.java index 6dda9506..e92a0241 100644 --- a/src/main/java/org/ject/support/admin/apply/repository/AdminApplyRepository.java +++ b/src/main/java/org/ject/support/admin/apply/repository/AdminApplyRepository.java @@ -11,16 +11,16 @@ public interface AdminApplyRepository extends JpaRepository, AdminApplyQueryRepository { - @Query("SELECT a FROM Apply a JOIN FETCH a.member LEFT JOIN FETCH a.applicationForm WHERE a.id = :id") - Optional findByIdWithMember(@Param("id") Long id); + @Query("SELECT a FROM Apply a JOIN FETCH a.applicant LEFT JOIN FETCH a.applicationForm WHERE a.id = :id") + Optional findByIdWithApplicant(@Param("id") Long id); - @Query("SELECT a FROM Apply a JOIN FETCH a.member LEFT JOIN FETCH a.applicationForm WHERE a.id IN :ids") - List findAllByIdWithMember(@Param("ids") List ids); + @Query("SELECT a FROM Apply a JOIN FETCH a.applicant LEFT JOIN FETCH a.applicationForm WHERE a.id IN :ids") + List findAllByIdWithApplicant(@Param("ids") List ids); @Modifying(clearAutomatically = true) @Query("UPDATE Apply a SET a.isDeleted = true WHERE a.id IN :ids") void deleteAllByIds(@Param("ids") Iterable ids); - @Query("select a from Apply a join fetch a.member m where a.id = :applyId and a.status = :status") - Optional findByIdAndStatusWithMember(@Param("applyId") Long applyId, @Param("status") ApplyStatus status); + @Query("select a from Apply a join fetch a.applicant m where a.id = :applyId and a.status = :status") + Optional findByIdAndStatusWithApplicant(@Param("applyId") Long applyId, @Param("status") ApplyStatus status); } diff --git a/src/main/java/org/ject/support/admin/apply/service/AdminApplyService.java b/src/main/java/org/ject/support/admin/apply/service/AdminApplyService.java index e8d42ece..a6c7628c 100644 --- a/src/main/java/org/ject/support/admin/apply/service/AdminApplyService.java +++ b/src/main/java/org/ject/support/admin/apply/service/AdminApplyService.java @@ -10,6 +10,8 @@ import org.ject.support.admin.apply.repository.AdminApplyRepository; import org.ject.support.common.data.PageResponse; import org.ject.support.common.util.Map2JsonSerializer; +import org.ject.support.domain.applicant.entity.Applicant; +import org.ject.support.domain.applicant.entity.ApplicantEditor; import org.ject.support.domain.apply.domain.Apply; import org.ject.support.domain.apply.domain.ApplyStatus; import org.ject.support.domain.apply.domain.Portfolio; @@ -17,8 +19,6 @@ import org.ject.support.domain.apply.exception.ApplyErrorCode; import org.ject.support.domain.apply.exception.ApplyException; import org.ject.support.domain.apply.repository.ApplyRepository; -import org.ject.support.domain.member.entity.Member; -import org.ject.support.domain.member.entity.MemberEditor; import org.ject.support.domain.recruit.domain.Recruit; import org.ject.support.domain.recruit.exception.QuestionErrorCode; import org.ject.support.domain.recruit.exception.QuestionException; @@ -58,17 +58,17 @@ public AdminApplyDetailResponse findApply(final Long applyId, @Transactional public void updateSubmittedApply(final Long applyId, final SubmittedApplyEditRequest request) { - Apply apply = adminApplyRepository.findByIdAndStatusWithMember(applyId, ApplyStatus.SUBMITTED) + Apply apply = adminApplyRepository.findByIdAndStatusWithApplicant(applyId, ApplyStatus.SUBMITTED) .orElseThrow(() -> new ApplyException(ApplyErrorCode.NOT_FOUND_APPLY)); - Member member = apply.getMember(); - MemberEditor memberEditor = member.toEditor() + Applicant applicant = apply.getApplicant(); + ApplicantEditor applicantEditor = applicant.toEditor() .name(request.name()) .phoneNumber(request.phoneNumber()) .email(request.email()) .jobFamily(request.jobFamily()) .build(); - member.edit(memberEditor); + applicant.edit(applicantEditor); Map answers = request.answers(); validateQuestions(answers, apply.getRecruit()); @@ -85,7 +85,7 @@ public void updateSubmittedApply(final Long applyId, @Transactional public void deleteApply(final Long applyId) { - Apply apply = adminApplyRepository.findByIdWithMember(applyId) + Apply apply = adminApplyRepository.findByIdWithApplicant(applyId) .orElseThrow(() -> new ApplyException(ApplyErrorCode.NOT_FOUND_APPLY)); adminApplyRepository.delete(apply); } diff --git a/src/main/java/org/ject/support/admin/apply/service/ApplyPassService.java b/src/main/java/org/ject/support/admin/apply/service/ApplyPassService.java index 12dff00e..a2eb5a03 100644 --- a/src/main/java/org/ject/support/admin/apply/service/ApplyPassService.java +++ b/src/main/java/org/ject/support/admin/apply/service/ApplyPassService.java @@ -1,10 +1,10 @@ package org.ject.support.admin.apply.service; import lombok.RequiredArgsConstructor; +import org.ject.support.domain.applicant.entity.Applicant; import org.ject.support.domain.apply.domain.Apply; import org.ject.support.domain.apply.exception.ApplyException; import org.ject.support.domain.apply.repository.ApplyRepository; -import org.ject.support.domain.member.entity.Member; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; @@ -21,7 +21,7 @@ public class ApplyPassService { @Transactional public int passApply(List applyIds) { // 지원 정보 조회 - List applies = applyRepository.findAllByIdWithMember(applyIds); + List applies = applyRepository.findAllByIdWithApplicant(applyIds); // 지원자 합격 처리 applies.forEach(apply -> { @@ -31,9 +31,9 @@ public int passApply(List applyIds) { } // 지원자 role 승격 - Member member = apply.getMember(); - member.promoteToSemester(); - member.updateMemberType(apply.getRecruit().getRecruitType().toMemberType()); + Applicant applicant = apply.getApplicant(); + applicant.promoteToSemester(); + applicant.updateMemberType(apply.getRecruit().getRecruitType().toMemberType()); }); // 승인한 구성원 수 반환 diff --git a/src/main/java/org/ject/support/domain/applicant/repository/ApplicantQueryRepositoryImpl.java b/src/main/java/org/ject/support/domain/applicant/repository/ApplicantQueryRepositoryImpl.java index 92ec6d26..db24ff60 100644 --- a/src/main/java/org/ject/support/domain/applicant/repository/ApplicantQueryRepositoryImpl.java +++ b/src/main/java/org/ject/support/domain/applicant/repository/ApplicantQueryRepositoryImpl.java @@ -30,7 +30,7 @@ public List findEmailsByIdsAndNotSubmitted(List applicantIds) { return queryFactory.select(applicant.email) .from(applicant) .join(apply) - .on(applicant.id.eq(apply.member.id)) + .on(applicant.id.eq(apply.applicant.id)) .where(applicant.id.in(applicantIds), applicant.isDeleted.eq(false), apply.status.eq(SUBMITTED).not()) diff --git a/src/main/java/org/ject/support/domain/apply/controller/ApplyApiSpec.java b/src/main/java/org/ject/support/domain/apply/controller/ApplyApiSpec.java index e0f289b7..eeacd521 100644 --- a/src/main/java/org/ject/support/domain/apply/controller/ApplyApiSpec.java +++ b/src/main/java/org/ject/support/domain/apply/controller/ApplyApiSpec.java @@ -18,26 +18,26 @@ public interface ApplyApiSpec { @Operation( summary = "지원서 임시 저장 조회", description = "지원자의 특정 공고에 대한 임시 저장 지원서를 조회합니다.") - TempApplicationFormResponse findTempApplicationForm(@AuthPrincipal Long memberId, + TempApplicationFormResponse findTempApplicationForm(@AuthPrincipal Long applicantId, @RequestParam Long recruitId); @Operation( summary = "지원서 임시 저장", description = "지원자의 특정 공고에 대한 지원서를 임시 저장합니다.") - void saveApplicationTemporarily(@AuthPrincipal Long memberId, + void saveApplicationTemporarily(@AuthPrincipal Long applicantId, @RequestParam Long recruitId, @RequestBody ApplyTemporaryRequest request); @Operation( summary = "지원서 초기화", description = "지원자의 특정 공고에 대한 프로필과 임시 지원서를 제거합니다.") - void deleteProfileAndTempApplicationForm(@AuthPrincipal Long memberId, + void deleteProfileAndTempApplicationForm(@AuthPrincipal Long applicantId, @RequestParam Long recruitId); @Operation( summary = "지원서 제출", description = "지원자의 특정 공고에 대해 작성이 완료된 지원서를 제출합니다.") - void submitApplication(@AuthPrincipal Long memberId, + void submitApplication(@AuthPrincipal Long applicantId, @RequestParam Long recruitId, @RequestBody SubmitApplicationRequest request); @@ -49,14 +49,14 @@ void submitApplication(@AuthPrincipal Long memberId, - TEMP_SAVED: 작성 중인 지원서가 있는 경우 - SUBMITTED: 이미 지원서를 제출한 경우 """) - ApplyStatusResponse checkApplyStatus(@AuthPrincipal Long memberId, + ApplyStatusResponse checkApplyStatus(@AuthPrincipal Long applicantId, @RequestParam Long recruitId); @Operation( summary = "프로필 작성(저장)", description = "지원자의 특정 공고에 대한 프로필을 작성(저장)합니다." ) - void saveProfile(@AuthPrincipal Long memberId, + void saveProfile(@AuthPrincipal Long applicantId, @RequestParam Long recruitId, @RequestBody @Valid ApplyProfileRequest request); } diff --git a/src/main/java/org/ject/support/domain/apply/controller/ApplyController.java b/src/main/java/org/ject/support/domain/apply/controller/ApplyController.java index 01e8de20..61fb2d01 100644 --- a/src/main/java/org/ject/support/domain/apply/controller/ApplyController.java +++ b/src/main/java/org/ject/support/domain/apply/controller/ApplyController.java @@ -27,52 +27,52 @@ public class ApplyController implements ApplyApiSpec { @Override @GetMapping("/temp") @PreAuthorize("hasRole('ROLE_APPLY')") - public TempApplicationFormResponse findTempApplicationForm(@AuthPrincipal Long memberId, + public TempApplicationFormResponse findTempApplicationForm(@AuthPrincipal Long applicantId, @RequestParam Long recruitId) { - return applyUsecase.findTempApplicationForm(memberId, recruitId); + return applyUsecase.findTempApplicationForm(applicantId, recruitId); } @Override @PostMapping("/temp") @PreAuthorize("hasRole('ROLE_APPLY')") - public void saveApplicationTemporarily(@AuthPrincipal Long memberId, + public void saveApplicationTemporarily(@AuthPrincipal Long applicantId, @RequestParam Long recruitId, @RequestBody ApplyTemporaryRequest request) { - applyUsecase.saveApplicationTemporarily(memberId, recruitId, request.answers(), request.portfolios()); + applyUsecase.saveApplicationTemporarily(applicantId, recruitId, request.answers(), request.portfolios()); } @Override @DeleteMapping("/temp") @PreAuthorize("hasRole('ROLE_APPLY')") - public void deleteProfileAndTempApplicationForm(@AuthPrincipal Long memberId, + public void deleteProfileAndTempApplicationForm(@AuthPrincipal Long applicantId, @RequestParam Long recruitId) { - applyUsecase.deleteProfileAndTempApplicationForm(memberId, recruitId); + applyUsecase.deleteProfileAndTempApplicationForm(applicantId, recruitId); } @Override @PostMapping("/submit") @PreAuthorize("hasRole('ROLE_APPLY')") - public void submitApplication(@AuthPrincipal Long memberId, + public void submitApplication(@AuthPrincipal Long applicantId, @RequestParam Long recruitId, @RequestBody SubmitApplicationRequest request) { - applyUsecase.submitApplication(memberId, recruitId, request.answers(), request.portfolios()); + applyUsecase.submitApplication(applicantId, recruitId, request.answers(), request.portfolios()); } @Override @GetMapping("/status") @PreAuthorize("hasRole('ROLE_APPLY')") - public ApplyStatusResponse checkApplyStatus(@AuthPrincipal Long memberId, + public ApplyStatusResponse checkApplyStatus(@AuthPrincipal Long applicantId, @RequestParam Long recruitId) { - return applyUsecase.checkApplyStatus(memberId, recruitId); + return applyUsecase.checkApplyStatus(applicantId, recruitId); } @Override @PostMapping("/profile") @PreAuthorize("hasRole('ROLE_APPLY')") - public void saveProfile(@AuthPrincipal Long memberId, + public void saveProfile(@AuthPrincipal Long applicantId, @RequestParam Long recruitId, @RequestBody @Valid ApplyProfileRequest request ) { - applyUsecase.saveProfile(memberId, recruitId, request); + applyUsecase.saveProfile(applicantId, recruitId, request); } } diff --git a/src/main/java/org/ject/support/domain/apply/domain/Apply.java b/src/main/java/org/ject/support/domain/apply/domain/Apply.java index 2c734450..76a61cd6 100644 --- a/src/main/java/org/ject/support/domain/apply/domain/Apply.java +++ b/src/main/java/org/ject/support/domain/apply/domain/Apply.java @@ -20,10 +20,10 @@ import lombok.NoArgsConstructor; import org.hibernate.annotations.SQLDelete; import org.hibernate.annotations.SQLRestriction; +import org.ject.support.domain.applicant.entity.Applicant; import org.ject.support.domain.apply.exception.ApplyErrorCode; import org.ject.support.domain.apply.exception.ApplyException; import org.ject.support.domain.base.BaseTimeEntity; -import org.ject.support.domain.member.entity.Member; import org.ject.support.domain.recruit.domain.Recruit; @Entity @@ -40,8 +40,8 @@ public class Apply extends BaseTimeEntity { private Long id; @ManyToOne(fetch = FetchType.LAZY) - @JoinColumn(name = "member_id", nullable = false) - private Member member; + @JoinColumn(name = "applicant_id", nullable = false) + private Applicant applicant; @ManyToOne(fetch = FetchType.LAZY) @JoinColumn(name = "recruit_id", nullable = false) @@ -65,9 +65,9 @@ public class Apply extends BaseTimeEntity { @Version private Long version; - public static Apply createApply(Member member, Recruit recruit) { + public static Apply createApply(Applicant applicant, Recruit recruit) { return Apply.builder() - .member(member) + .applicant(applicant) .recruit(recruit) .status(ApplyStatus.JOINED) .build(); diff --git a/src/main/java/org/ject/support/domain/apply/repository/ApplicationFormRepository.java b/src/main/java/org/ject/support/domain/apply/repository/ApplicationFormRepository.java index b0d7bdef..7769289d 100644 --- a/src/main/java/org/ject/support/domain/apply/repository/ApplicationFormRepository.java +++ b/src/main/java/org/ject/support/domain/apply/repository/ApplicationFormRepository.java @@ -13,7 +13,7 @@ public interface ApplicationFormRepository extends JpaRepository= :now and a.member_id = :memberId)", nativeQuery = true) - boolean existsByMemberId(@Param("memberId") Long memberId, - @Param("now") LocalDateTime now); + "WHERE r.start_date <= :now and r.end_date >= :now and a.applicant_id = :applicantId)", nativeQuery = true) + boolean existsByApplicantId(@Param("applicantId") Long applicantId, + @Param("now") LocalDateTime now); } diff --git a/src/main/java/org/ject/support/domain/apply/repository/ApplyRepository.java b/src/main/java/org/ject/support/domain/apply/repository/ApplyRepository.java index f595de0f..4495d501 100644 --- a/src/main/java/org/ject/support/domain/apply/repository/ApplyRepository.java +++ b/src/main/java/org/ject/support/domain/apply/repository/ApplyRepository.java @@ -13,32 +13,32 @@ public interface ApplyRepository extends JpaRepository { - @Query("select a from Apply a join a.recruit r where a.member.id = :memberId and r.id = :recruitId and r.startDate <= :now and r.endDate >= :now") - Optional findByMemberIdAndRecruitIdInActiveRecruit(@Param("memberId") Long memberId, - @Param("recruitId") Long recruitId, - @Param("now") LocalDateTime now); + @Query("select a from Apply a join a.recruit r where a.applicant.id = :applicantId and r.id = :recruitId and r.startDate <= :now and r.endDate >= :now") + Optional findByApplicantIdAndRecruitIdInActiveRecruit(@Param("applicantId") Long applicantId, + @Param("recruitId") Long recruitId, + @Param("now") LocalDateTime now); @Query(""" select exists ( select 1 from Apply a join a.recruit r - where a.member.id = :memberId and r.id = :recruitId and r.startDate <= :now and r.endDate >= :now + where a.applicant.id = :applicantId and r.id = :recruitId and r.startDate <= :now and r.endDate >= :now ) """) - boolean existsByMemberIdAndRecruitIdInActiveRecruit(@Param("memberId") Long memberId, - @Param("recruitId") Long recruitId, - @Param("now") LocalDateTime now); + boolean existsByApplicantIdAndRecruitIdInActiveRecruit(@Param("applicantId") Long applicantId, + @Param("recruitId") Long recruitId, + @Param("now") LocalDateTime now); List findByRecruitAndStatus(Recruit recruit, ApplyStatus status); - @Query("select a from Apply a join fetch a.member m where a.id = :applyId and a.status = :status") - Optional findByIdAndStatusWithMember(@Param("applyId") Long applyId, @Param("status") ApplyStatus status); + @Query("select a from Apply a join fetch a.applicant m where a.id = :applyId and a.status = :status") + Optional findByIdAndStatusWithApplicant(@Param("applyId") Long applyId, @Param("status") ApplyStatus status); - @Query("select a from Apply a join fetch a.member m where a.id in :applyIds and a.status = :status") - List findAllByIdAndStatusWithMember(@Param("applyIds") List applyIds, @Param("status") ApplyStatus status); + @Query("select a from Apply a join fetch a.applicant m where a.id in :applyIds and a.status = :status") + List findAllByIdAndStatusWithApplicant(@Param("applyIds") List applyIds, @Param("status") ApplyStatus status); @Query("select count(a) from Apply a where a.status = :status") Long countByStatus(@Param("status") ApplyStatus status); - @Query("SELECT a FROM Apply a JOIN FETCH a.member JOIN FETCH a.recruit WHERE a.id IN :ids") - List findAllByIdWithMember(@Param("ids") List ids); + @Query("SELECT a FROM Apply a JOIN FETCH a.applicant JOIN FETCH a.recruit WHERE a.id IN :ids") + List findAllByIdWithApplicant(@Param("ids") List ids); } diff --git a/src/main/java/org/ject/support/domain/apply/service/ApplyService.java b/src/main/java/org/ject/support/domain/apply/service/ApplyService.java index 8a14ece1..21c6b949 100644 --- a/src/main/java/org/ject/support/domain/apply/service/ApplyService.java +++ b/src/main/java/org/ject/support/domain/apply/service/ApplyService.java @@ -5,6 +5,10 @@ import org.ject.support.common.util.Map2JsonSerializer; import org.ject.support.common.util.PeriodAccessible; import org.ject.support.common.util.String2MapSerializer; +import org.ject.support.domain.applicant.entity.Applicant; +import org.ject.support.domain.applicant.exception.ApplicantErrorCode; +import org.ject.support.domain.applicant.exception.ApplicantException; +import org.ject.support.domain.applicant.repository.ApplicantRepository; import org.ject.support.domain.apply.domain.ApplicationForm; import org.ject.support.domain.apply.domain.Apply; import org.ject.support.domain.apply.domain.ApplyStatus; @@ -17,10 +21,6 @@ import org.ject.support.domain.apply.exception.ApplyException; import org.ject.support.domain.apply.repository.ApplicationFormRepository; import org.ject.support.domain.apply.repository.ApplyRepository; -import org.ject.support.domain.member.entity.Member; -import org.ject.support.domain.member.exception.MemberErrorCode; -import org.ject.support.domain.member.exception.MemberException; -import org.ject.support.domain.member.repository.MemberRepository; import org.ject.support.domain.recruit.domain.Recruit; import org.ject.support.domain.recruit.exception.QuestionErrorCode; import org.ject.support.domain.recruit.exception.QuestionException; @@ -52,15 +52,15 @@ public class ApplyService implements ApplyUsecase { private final RecruitRepository recruitRepository; private final ApplyRepository applyRepository; private final ApplicationFormRepository applicationFormRepository; - private final MemberRepository memberRepository; + private final ApplicantRepository applicantRepository; private final Map2JsonSerializer map2JsonSerializer; private final String2MapSerializer string2MapSerializer; @Override @PeriodAccessible(recruitIdParameterIndex = RECRUIT_ID_PARAMETER_INDEX) @Transactional(readOnly = true) - public TempApplicationFormResponse findTempApplicationForm(final Long memberId, final Long recruitId) { - Apply apply = applyRepository.findByMemberIdAndRecruitIdInActiveRecruit(memberId, recruitId, LocalDateTime.now()) + public TempApplicationFormResponse findTempApplicationForm(final Long applicantId, final Long recruitId) { + Apply apply = applyRepository.findByApplicantIdAndRecruitIdInActiveRecruit(applicantId, recruitId, LocalDateTime.now()) .orElseThrow(() -> new ApplyException(NOT_FOUND_APPLY)); if (apply.isNotTempSaved()) { @@ -80,12 +80,12 @@ public TempApplicationFormResponse findTempApplicationForm(final Long memberId, @Override @PeriodAccessible(recruitIdParameterIndex = RECRUIT_ID_PARAMETER_INDEX) @Transactional - public void saveApplicationTemporarily(Long memberId, + public void saveApplicationTemporarily(Long applicantId, Long recruitId, Map answers, List portfolios) { - // 1. memberId와 recruitId를 바탕으로 apply 조회 - Apply apply = applyRepository.findByMemberIdAndRecruitIdInActiveRecruit(memberId, recruitId, LocalDateTime.now()) + // 1. applicantId와 recruitId를 바탕으로 apply 조회 + Apply apply = applyRepository.findByApplicantIdAndRecruitIdInActiveRecruit(applicantId, recruitId, LocalDateTime.now()) .orElseThrow(() -> new ApplyException(NOT_FOUND_APPLY)); ApplyStatus applyStatus = apply.getStatus(); @@ -120,9 +120,9 @@ public void saveApplicationTemporarily(Long memberId, @Override @PeriodAccessible(recruitIdParameterIndex = RECRUIT_ID_PARAMETER_INDEX) @Transactional - public void deleteProfileAndTempApplicationForm(Long memberId, Long recruitId) { + public void deleteProfileAndTempApplicationForm(Long applicantId, Long recruitId) { // 지원 정보 조회 - Apply apply = applyRepository.findByMemberIdAndRecruitIdInActiveRecruit(memberId, recruitId, LocalDateTime.now()) + Apply apply = applyRepository.findByApplicantIdAndRecruitIdInActiveRecruit(applicantId, recruitId, LocalDateTime.now()) .orElseThrow(() -> new ApplyException(NOT_FOUND_APPLY)); // 지원서를 임시 저장하지 않은 경우 실패 @@ -135,21 +135,21 @@ public void deleteProfileAndTempApplicationForm(Long memberId, Long recruitId) { apply.updateStatus(JOINED); // 프로필 제거 - Member applicant = apply.getMember(); + Applicant applicant = apply.getApplicant(); applicant.deleteProfile(); } @Override @PeriodAccessible(recruitIdParameterIndex = RECRUIT_ID_PARAMETER_INDEX) @Transactional - public void submitApplication(Long memberId, + public void submitApplication(Long applicantId, Long recruitId, Map answers, List portfolios) { try { // 1. 지원 정보 조회 (낙관적 락 - @Version 기반 동시성 제어) - Apply apply = applyRepository.findByMemberIdAndRecruitIdInActiveRecruit( - memberId, recruitId, LocalDateTime.now()) + Apply apply = applyRepository.findByApplicantIdAndRecruitIdInActiveRecruit( + applicantId, recruitId, LocalDateTime.now()) .orElseThrow(() -> new ApplyException(NOT_FOUND_APPLY)); // 2. 지원양식과 answers의 key를 비교해 올바른 질문 양식인지 점검 @@ -174,7 +174,7 @@ public void submitApplication(Long memberId, // 명시적 flush를 통해 낙관적 락 충돌을 메서드 내부에서 감지하고 catch 블록으로 전달합니다. applyRepository.flush(); } catch (ObjectOptimisticLockingFailureException e) { - log.warn("지원서 제출 중 낙관적 락 충돌 발생. memberId: {}", memberId); + log.warn("지원서 제출 중 낙관적 락 충돌 발생. applicantId: {}", applicantId); throw new ApplyException(ALREADY_SUBMITTED); } } @@ -182,11 +182,11 @@ public void submitApplication(Long memberId, @Override @PeriodAccessible(recruitIdParameterIndex = RECRUIT_ID_PARAMETER_INDEX) @Transactional(readOnly = true) - public ApplyStatusResponse checkApplyStatus(Long memberId, Long recruitId) { - memberRepository.findById(memberId) - .orElseThrow(() -> new MemberException(MemberErrorCode.NOT_FOUND_MEMBER)); + public ApplyStatusResponse checkApplyStatus(Long applicantId, Long recruitId) { + applicantRepository.findById(applicantId) + .orElseThrow(() -> new ApplicantException(ApplicantErrorCode.NOT_FOUND_APPLICANT)); - return applyRepository.findByMemberIdAndRecruitIdInActiveRecruit(memberId, recruitId, LocalDateTime.now()) + return applyRepository.findByApplicantIdAndRecruitIdInActiveRecruit(applicantId, recruitId, LocalDateTime.now()) .map(ApplyStatusResponse::of) .orElseThrow(() -> new ApplyException(NOT_FOUND_APPLY)); } @@ -194,15 +194,15 @@ public ApplyStatusResponse checkApplyStatus(Long memberId, Long recruitId) { @Override @PeriodAccessible(recruitIdParameterIndex = RECRUIT_ID_PARAMETER_INDEX) @Transactional - public void saveProfile(Long memberId, Long recruitId, ApplyProfileRequest request) { - var member = memberRepository.findById(memberId) - .orElseThrow(() -> new MemberException(MemberErrorCode.NOT_FOUND_MEMBER)); + public void saveProfile(Long applicantId, Long recruitId, ApplyProfileRequest request) { + var applicant = applicantRepository.findById(applicantId) + .orElseThrow(() -> new ApplicantException(ApplicantErrorCode.NOT_FOUND_APPLICANT)); Recruit recruit = getActiveRecruit(recruitId); - createApplyIfNotExists(member, recruit); + createApplyIfNotExists(applicant, recruit); - var memberEditorBuilder = member.toEditor(); - var memberEditor = memberEditorBuilder + var applicantEditorBuilder = applicant.toEditor(); + var applicantEditor = applicantEditorBuilder .name(request.name()) .phoneNumber(request.phoneNumber()) .jobFamily(recruit.getJobFamily()) @@ -212,19 +212,19 @@ public void saveProfile(Long memberId, Long recruitId, ApplyProfileRequest reque .interestedDomains(request.interestedDomains()) .build(); - member.edit(memberEditor); + applicant.edit(applicantEditor); } - private void createApplyIfNotExists(Member member, Recruit recruit) { - boolean exists = applyRepository.existsByMemberIdAndRecruitIdInActiveRecruit( - member.getId(), recruit.getId(), LocalDateTime.now()); + private void createApplyIfNotExists(Applicant applicant, Recruit recruit) { + boolean exists = applyRepository.existsByApplicantIdAndRecruitIdInActiveRecruit( + applicant.getId(), recruit.getId(), LocalDateTime.now()); if (!exists) { try { - var newApply = Apply.createApply(member, recruit); + var newApply = Apply.createApply(applicant, recruit); applyRepository.save(newApply); } catch (DataIntegrityViolationException e) { - log.warn("지원서 생성 중 중복 생성 충돌 발생. memberId: {}, recruitId: {}. 이미 지원서가 존재합니다.", - member.getId(), recruit.getId()); + log.warn("지원서 생성 중 중복 생성 충돌 발생. applicantId: {}, recruitId: {}. 이미 지원서가 존재합니다.", + applicant.getId(), recruit.getId()); } } } diff --git a/src/main/java/org/ject/support/domain/apply/service/ApplyUsecase.java b/src/main/java/org/ject/support/domain/apply/service/ApplyUsecase.java index 1593674b..f3f5907d 100644 --- a/src/main/java/org/ject/support/domain/apply/service/ApplyUsecase.java +++ b/src/main/java/org/ject/support/domain/apply/service/ApplyUsecase.java @@ -9,22 +9,22 @@ import java.util.Map; public interface ApplyUsecase { - TempApplicationFormResponse findTempApplicationForm(Long memberId, Long recruitId); + TempApplicationFormResponse findTempApplicationForm(Long applicantId, Long recruitId); - void saveApplicationTemporarily(Long memberId, + void saveApplicationTemporarily(Long applicantId, Long recruitId, Map answers, List portfolios); - void deleteProfileAndTempApplicationForm(Long memberId, Long recruitId); + void deleteProfileAndTempApplicationForm(Long applicantId, Long recruitId); - void submitApplication(Long memberId, + void submitApplication(Long applicantId, Long recruitId, Map answers, List portfolios); - ApplyStatusResponse checkApplyStatus(Long memberId, Long recruitId); + ApplyStatusResponse checkApplyStatus(Long applicantId, Long recruitId); - void saveProfile(Long memberId, Long recruitId, ApplyProfileRequest request); + void saveProfile(Long applicantId, Long recruitId, ApplyProfileRequest request); } diff --git a/src/main/java/org/ject/support/domain/apply/service/RemindApplyService.java b/src/main/java/org/ject/support/domain/apply/service/RemindApplyService.java index d716c7e4..5437595c 100644 --- a/src/main/java/org/ject/support/domain/apply/service/RemindApplyService.java +++ b/src/main/java/org/ject/support/domain/apply/service/RemindApplyService.java @@ -2,9 +2,9 @@ import lombok.RequiredArgsConstructor; import org.ject.support.common.util.DateTimeUtil; +import org.ject.support.domain.applicant.repository.ApplicantRepository; import org.ject.support.domain.apply.domain.ApplyStatus; import org.ject.support.domain.apply.repository.ApplyRepository; -import org.ject.support.domain.member.repository.MemberRepository; import org.ject.support.domain.recruit.domain.Recruit; import org.ject.support.domain.recruit.repository.RecruitRepository; import org.ject.support.external.email.domain.EmailTemplate; @@ -22,7 +22,7 @@ public class RemindApplyService implements RemindApplyUsecase { private static final String EMAIL_SEND_GROUP = "remind_apply"; private final RecruitRepository recruitRepository; - private final MemberRepository memberRepository; + private final ApplicantRepository applicantRepository; private final ApplyRepository applyRepository; private final SesEmailSendService emailSendService; @@ -34,11 +34,11 @@ public void remindApply(Long recruitId) { // 모집 기간 중 지원서 임시 저장한 지원자 ID 모두 조회 List applicantIds = applyRepository.findByRecruitAndStatus(recruit, ApplyStatus.TEMP_SAVED) .stream() - .map(apply -> apply.getMember().getId()) + .map(apply -> apply.getApplicant().getId()) .toList(); // 지원서 최종 제출하지 않은 지원자 필터링 - List targetEmails = memberRepository.findEmailsByIdsAndNotSubmitted(applicantIds); + List targetEmails = applicantRepository.findEmailsByIdsAndNotSubmitted(applicantIds); // 필터링한 지원자들에게 리마인드 emailSendService.sendBulkTemplatedEmail( diff --git a/src/main/java/org/ject/support/domain/member/dto/MemberAccountProjection.java b/src/main/java/org/ject/support/domain/member/dto/MemberAccountProjection.java deleted file mode 100644 index 4433f88a..00000000 --- a/src/main/java/org/ject/support/domain/member/dto/MemberAccountProjection.java +++ /dev/null @@ -1,13 +0,0 @@ -package org.ject.support.domain.member.dto; - -import org.ject.support.domain.member.MemberStatus; -import org.ject.support.domain.member.Role; - -public record MemberAccountProjection( - Long id, - String email, - String name, - Role role, - MemberStatus status -) { -} diff --git a/src/main/java/org/ject/support/domain/member/repository/MemberQueryRepository.java b/src/main/java/org/ject/support/domain/member/repository/MemberQueryRepository.java index 4e0d9450..5273fc2f 100644 --- a/src/main/java/org/ject/support/domain/member/repository/MemberQueryRepository.java +++ b/src/main/java/org/ject/support/domain/member/repository/MemberQueryRepository.java @@ -1,18 +1,8 @@ package org.ject.support.domain.member.repository; -import org.ject.support.admin.account.dto.AdminAccountSearchCondition; -import org.ject.support.domain.member.dto.MemberAccountProjection; import org.ject.support.domain.member.dto.TeamMemberNames; -import org.springframework.data.domain.Page; -import org.springframework.data.domain.Pageable; - -import java.util.List; public interface MemberQueryRepository { TeamMemberNames findMemberNamesByTeamId(Long teamId); - - List findEmailsByIdsAndNotSubmitted(List applicantIds); - - Page findAccounts(AdminAccountSearchCondition condition, Pageable pageable); } diff --git a/src/main/java/org/ject/support/domain/member/repository/MemberQueryRepositoryImpl.java b/src/main/java/org/ject/support/domain/member/repository/MemberQueryRepositoryImpl.java index 69998564..fdfe6410 100644 --- a/src/main/java/org/ject/support/domain/member/repository/MemberQueryRepositoryImpl.java +++ b/src/main/java/org/ject/support/domain/member/repository/MemberQueryRepositoryImpl.java @@ -1,7 +1,5 @@ package org.ject.support.domain.member.repository; -import static org.ject.support.domain.apply.domain.ApplyStatus.SUBMITTED; -import static org.ject.support.domain.apply.domain.QApply.apply; import static org.ject.support.domain.member.JobFamily.BE; import static org.ject.support.domain.member.JobFamily.FE; import static org.ject.support.domain.member.JobFamily.PD; @@ -10,22 +8,11 @@ import static org.ject.support.domain.member.entity.QTeamMember.teamMember; import com.querydsl.core.group.GroupBy; -import com.querydsl.core.types.Projections; import com.querydsl.core.types.dsl.CaseBuilder; -import com.querydsl.core.types.dsl.BooleanExpression; import com.querydsl.jpa.JPQLQueryFactory; -import java.util.List; -import java.util.Optional; import lombok.RequiredArgsConstructor; -import org.ject.support.admin.account.dto.AdminAccountSearchCondition; -import org.ject.support.common.data.PageResponse; -import org.ject.support.domain.member.MemberStatus; -import org.ject.support.domain.member.Role; -import org.ject.support.domain.member.dto.MemberAccountProjection; import org.ject.support.domain.member.dto.QTeamMemberNames; import org.ject.support.domain.member.dto.TeamMemberNames; -import org.springframework.data.domain.Page; -import org.springframework.data.domain.Pageable; import org.springframework.stereotype.Repository; @Repository @@ -63,67 +50,4 @@ public TeamMemberNames findMemberNamesByTeamId(Long teamId) { .otherwise((String) null)) ))).get(teamId); } - - @Override - public List findEmailsByIdsAndNotSubmitted(List applicantIds) { - return queryFactory.select(member.email) - .from(member) - .join(apply) - .on(member.id.eq(apply.member.id)) - .where(member.id.in(applicantIds), - member.isDeleted.eq(false), - apply.status.eq(SUBMITTED).not()) - .fetch(); - } - - @Override - public Page findAccounts(final AdminAccountSearchCondition condition, - final Pageable pageable) { - List content = queryFactory - .select(Projections.constructor( - MemberAccountProjection.class, - member.id, - member.email, - member.name, - member.role, - member.status)) - .from(member) - .where( - member.isDeleted.eq(false), - member.role.in(Role.backofficeRoles()), - inRoles(condition.roles()), - inStatuses(condition.statuses()) - ) - .orderBy(member.createdAt.desc(), member.id.desc()) - .offset(pageable.getOffset()) - .limit(pageable.getPageSize()) - .fetch(); - - Long total = queryFactory - .select(member.count()) - .from(member) - .where( - member.isDeleted.eq(false), - member.role.in(Role.backofficeRoles()), - inRoles(condition.roles()), - inStatuses(condition.statuses()) - ) - .fetchOne(); - - return PageResponse.from(content, pageable, total != null ? total : 0L); - } - - private BooleanExpression inRoles(final List roles) { - return Optional.ofNullable(roles) - .filter(values -> !values.isEmpty()) - .map(member.role::in) - .orElse(null); - } - - private BooleanExpression inStatuses(final List statuses) { - return Optional.ofNullable(statuses) - .filter(values -> !values.isEmpty()) - .map(member.status::in) - .orElse(null); - } } diff --git a/src/main/resources/db/migration/V32__convert_apply_member_to_applicant.sql b/src/main/resources/db/migration/V32__convert_apply_member_to_applicant.sql new file mode 100644 index 00000000..faf1b545 --- /dev/null +++ b/src/main/resources/db/migration/V32__convert_apply_member_to_applicant.sql @@ -0,0 +1,15 @@ +-- 지원도메인에서 member_id -> applicant_id로 컬럼,제약조건 변경 +ALTER TABLE apply + DROP FOREIGN KEY fk_apply_member; + +ALTER TABLE apply + DROP INDEX uk_apply_member_recruit; + +ALTER TABLE apply + CHANGE COLUMN member_id applicant_id BIGINT NOT NULL; + +ALTER TABLE apply + ADD CONSTRAINT fk_apply_applicant FOREIGN KEY (applicant_id) REFERENCES applicant (id) ON DELETE NO ACTION; + +ALTER TABLE apply + ADD CONSTRAINT uk_apply_applicant_recruit UNIQUE (applicant_id, recruit_id); diff --git a/src/test/java/org/ject/support/admin/apply/repository/AdminApplyQueryRepositoryTest.java b/src/test/java/org/ject/support/admin/apply/repository/AdminApplyQueryRepositoryTest.java index 6a568cce..d18bdaab 100644 --- a/src/test/java/org/ject/support/admin/apply/repository/AdminApplyQueryRepositoryTest.java +++ b/src/test/java/org/ject/support/admin/apply/repository/AdminApplyQueryRepositoryTest.java @@ -17,8 +17,8 @@ import org.ject.support.domain.member.JobFamily; import org.ject.support.domain.member.MemberStatus; import org.ject.support.domain.member.Role; -import org.ject.support.domain.member.entity.Member; -import org.ject.support.domain.member.repository.MemberRepository; +import org.ject.support.domain.applicant.entity.Applicant; +import org.ject.support.domain.applicant.repository.ApplicantRepository; import org.ject.support.domain.recruit.domain.Recruit; import org.ject.support.domain.recruit.domain.RecruitType; import org.ject.support.domain.recruit.domain.RecruitTypeDetail; @@ -49,7 +49,7 @@ class AdminApplyQueryRepositoryTest { RecruitRepository recruitRepository; @Autowired - MemberRepository memberRepository; + ApplicantRepository applicantRepository; @Autowired org.ject.support.domain.apply.repository.ApplyRepository applyRepository; @@ -75,14 +75,14 @@ void setUp() { @Test void JobFamily로_제출된_지원서_목록_조회_성공() { // given - Member beMember1 = createMember("be1@test.com", BE); - Member beMember2 = createMember("be2@test.com", BE); - Member feMember = createMember("fe@test.com", FE); - memberRepository.saveAll(List.of(beMember1, beMember2, feMember)); - - Apply beApply1 = getApply(beMember1, beRecruit, SUBMITTED); - Apply beApply2 = getApply(beMember2, beRecruit, SUBMITTED); - Apply feApply = getApply(feMember, feRecruit, SUBMITTED); + Applicant beApplicant1 = createApplicant("be1@test.com", BE); + Applicant beApplicant2 = createApplicant("be2@test.com", BE); + Applicant feApplicant = createApplicant("fe@test.com", FE); + applicantRepository.saveAll(List.of(beApplicant1, beApplicant2, feApplicant)); + + Apply beApply1 = getApply(beApplicant1, beRecruit, SUBMITTED); + Apply beApply2 = getApply(beApplicant2, beRecruit, SUBMITTED); + Apply feApply = getApply(feApplicant, feRecruit, SUBMITTED); applyRepository.saveAll(List.of(beApply1, beApply2, feApply)); Pageable pageable = PageRequest.of(0, 15); @@ -95,21 +95,21 @@ void setUp() { assertThat(result.getContent()).hasSize(2); assertThat(result.getTotalElements()).isEqualTo(2); assertThat(result.getContent()) - .extracting(Apply::getMember) - .containsExactlyInAnyOrder(beMember1, beMember2); + .extracting(Apply::getApplicant) + .containsExactlyInAnyOrder(beApplicant1, beApplicant2); } @Test void JobFamily가_null이면_전체_제출된_지원서_조회() { // given - Member beMember = createMember("be@test.com", BE); - Member feMember = createMember("fe@test.com", FE); - Member pmMember = createMember("pm@test.com", PM); - memberRepository.saveAll(List.of(beMember, feMember, pmMember)); - - Apply beApply = getApply(beMember, beRecruit, SUBMITTED); - Apply feApply = getApply(feMember, feRecruit, SUBMITTED); - Apply pmApply = getApply(pmMember, pmRecruit, SUBMITTED); + Applicant beApplicant = createApplicant("be@test.com", BE); + Applicant feApplicant = createApplicant("fe@test.com", FE); + Applicant pmApplicant = createApplicant("pm@test.com", PM); + applicantRepository.saveAll(List.of(beApplicant, feApplicant, pmApplicant)); + + Apply beApply = getApply(beApplicant, beRecruit, SUBMITTED); + Apply feApply = getApply(feApplicant, feRecruit, SUBMITTED); + Apply pmApply = getApply(pmApplicant, pmRecruit, SUBMITTED); applyRepository.saveAll(List.of(beApply, feApply, pmApply)); Pageable pageable = PageRequest.of(0, 15); @@ -126,12 +126,12 @@ void setUp() { @Test void 임시저장_상태는_조회되지_않음() { // given - Member beMember1 = createMember("be1@test.com", BE); - Member beMember2 = createMember("be2@test.com", BE); - memberRepository.saveAll(List.of(beMember1, beMember2)); + Applicant beApplicant1 = createApplicant("be1@test.com", BE); + Applicant beApplicant2 = createApplicant("be2@test.com", BE); + applicantRepository.saveAll(List.of(beApplicant1, beApplicant2)); - Apply submittedApply = getApply(beMember1, beRecruit, SUBMITTED); - Apply tempApply = getApply(beMember2, beRecruit, TEMP_SAVED); + Apply submittedApply = getApply(beApplicant1, beRecruit, SUBMITTED); + Apply tempApply = getApply(beApplicant2, beRecruit, TEMP_SAVED); applyRepository.saveAll(List.of(submittedApply, tempApply)); Pageable pageable = PageRequest.of(0, 15); @@ -148,13 +148,13 @@ void setUp() { @Test void 삭제된_회원의_지원서는_조회되지_않음() { // given - Member activeMember = createMember("active@test.com", BE); - Member deletedMember = createMember("deleted@test.com", BE); - deletedMember.deleteProfile(); - memberRepository.saveAll(List.of(activeMember, deletedMember)); + Applicant activeApplicant = createApplicant("active@test.com", BE); + Applicant deletedApplicant = createApplicant("deleted@test.com", BE); + deletedApplicant.deleteProfile(); + applicantRepository.saveAll(List.of(activeApplicant, deletedApplicant)); - Apply activeApply = getApply(activeMember, beRecruit, SUBMITTED); - Apply deletedApply = getApply(deletedMember, beRecruit, SUBMITTED); + Apply activeApply = getApply(activeApplicant, beRecruit, SUBMITTED); + Apply deletedApply = getApply(deletedApplicant, beRecruit, SUBMITTED); applyRepository.saveAll(List.of(activeApply, deletedApply)); Pageable pageable = PageRequest.of(0, 15); @@ -165,16 +165,16 @@ void setUp() { // then assertThat(result.getContent()).hasSize(1); - assertThat(result.getContent().getFirst().getMember()).isEqualTo(activeMember); + assertThat(result.getContent().getFirst().getApplicant()).isEqualTo(activeApplicant); } @Test void 페이징_정상_작동() { // given for (int i = 1; i <= 25; i++) { - Member member = createMember("be" + i + "@test.com", BE); - memberRepository.save(member); - Apply apply = getApply(member, beRecruit, SUBMITTED); + Applicant applicant = createApplicant("be" + i + "@test.com", BE); + applicantRepository.save(applicant); + Apply apply = getApply(applicant, beRecruit, SUBMITTED); applyRepository.save(apply); } @@ -209,18 +209,18 @@ void setUp() { @Test void createdAt_기준_내림차순_정렬() { // given - Member member1 = createMember("be1@test.com", BE); - Member member2 = createMember("be2@test.com", BE); - Member member3 = createMember("be3@test.com", BE); - memberRepository.saveAll(List.of(member1, member2, member3)); + Applicant applicant1 = createApplicant("be1@test.com", BE); + Applicant applicant2 = createApplicant("be2@test.com", BE); + Applicant applicant3 = createApplicant("be3@test.com", BE); + applicantRepository.saveAll(List.of(applicant1, applicant2, applicant3)); - Apply apply1 = getApply(member1, beRecruit, SUBMITTED); + Apply apply1 = getApply(applicant1, beRecruit, SUBMITTED); applyRepository.save(apply1); - Apply apply2 = getApply(member2, beRecruit, SUBMITTED); + Apply apply2 = getApply(applicant2, beRecruit, SUBMITTED); applyRepository.save(apply2); - Apply apply3 = getApply(member3, beRecruit, SUBMITTED); + Apply apply3 = getApply(applicant3, beRecruit, SUBMITTED); applyRepository.save(apply3); Pageable pageable = PageRequest.of(0, 15); @@ -258,12 +258,12 @@ void setUp() { .jobFamily(BE) .build()); - Member member1 = createMember("be1@test.com", BE); - Member member2 = createMember("be2@test.com", BE); - memberRepository.saveAll(List.of(member1, member2)); + Applicant applicant1 = createApplicant("be1@test.com", BE); + Applicant applicant2 = createApplicant("be2@test.com", BE); + applicantRepository.saveAll(List.of(applicant1, applicant2)); - Apply apply1 = getApply(member1, recruit1, SUBMITTED); - Apply apply2 = getApply(member2, recruit2, SUBMITTED); + Apply apply1 = getApply(applicant1, recruit1, SUBMITTED); + Apply apply2 = getApply(applicant2, recruit2, SUBMITTED); applyRepository.saveAll(List.of(apply1, apply2)); Pageable pageable = PageRequest.of(0, 15); @@ -297,12 +297,12 @@ void setUp() { .recruitType(RecruitType.BACKFILL) .build()); - Member member1 = createMember("regular@test.com", BE); - Member member2 = createMember("backfill@test.com", BE); - memberRepository.saveAll(List.of(member1, member2)); + Applicant applicant1 = createApplicant("regular@test.com", BE); + Applicant applicant2 = createApplicant("backfill@test.com", BE); + applicantRepository.saveAll(List.of(applicant1, applicant2)); - Apply apply1 = getApply(member1, regularRecruit, SUBMITTED); - Apply apply2 = getApply(member2, backfillRecruit, SUBMITTED); + Apply apply1 = getApply(applicant1, regularRecruit, SUBMITTED); + Apply apply2 = getApply(applicant2, backfillRecruit, SUBMITTED); applyRepository.saveAll(List.of(apply1, apply2)); Pageable pageable = PageRequest.of(0, 15); @@ -338,12 +338,12 @@ void setUp() { .recruitTypeDetail(RecruitTypeDetail.REFILL) .build()); - Member regularMember = createMember("regular-detail@test.com", BE); - Member refillMember = createMember("refill-detail@test.com", BE); - memberRepository.saveAll(List.of(regularMember, refillMember)); + Applicant regularApplicant = createApplicant("regular-detail@test.com", BE); + Applicant refillApplicant = createApplicant("refill-detail@test.com", BE); + applicantRepository.saveAll(List.of(regularApplicant, refillApplicant)); - Apply regularApply = getApply(regularMember, regularRecruit, SUBMITTED); - Apply refillApply = getApply(refillMember, refillRecruit, SUBMITTED); + Apply regularApply = getApply(regularApplicant, regularRecruit, SUBMITTED); + Apply refillApply = getApply(refillApplicant, refillRecruit, SUBMITTED); applyRepository.saveAll(List.of(regularApply, refillApply)); Pageable pageable = PageRequest.of(0, 15); @@ -362,12 +362,12 @@ void setUp() { @Test void recruitId로_제출된_지원서_필터링_조회() { // given - Member beMember = createMember("be-recruit-id@test.com", BE); - Member feMember = createMember("fe-recruit-id@test.com", FE); - memberRepository.saveAll(List.of(beMember, feMember)); + Applicant beApplicant = createApplicant("be-recruit-id@test.com", BE); + Applicant feApplicant = createApplicant("fe-recruit-id@test.com", FE); + applicantRepository.saveAll(List.of(beApplicant, feApplicant)); - Apply beApply = getApply(beMember, beRecruit, SUBMITTED); - Apply feApply = getApply(feMember, feRecruit, SUBMITTED); + Apply beApply = getApply(beApplicant, beRecruit, SUBMITTED); + Apply feApply = getApply(feApplicant, feRecruit, SUBMITTED); applyRepository.saveAll(List.of(beApply, feApply)); Pageable pageable = PageRequest.of(0, 15); @@ -392,13 +392,13 @@ private Recruit getRecruit(JobFamily jobFamily) { .build(); } - private Apply getApply(Member member, Recruit recruit, ApplyStatus status) { + private Apply getApply(Applicant applicant, Recruit recruit, ApplyStatus status) { ApplicationForm applicationForm = ApplicationForm.builder() .build(); Apply apply = Apply.builder() .recruit(recruit) - .member(member) + .applicant(applicant) .status(status) .applicationForm(applicationForm) .build(); @@ -408,8 +408,8 @@ private Apply getApply(Member member, Recruit recruit, ApplyStatus status) { return apply; } - private Member createMember(String email, JobFamily jobFamily) { - return Member.builder() + private Applicant createApplicant(String email, JobFamily jobFamily) { + return Applicant.builder() .email(email) .jobFamily(jobFamily) .semesterId(1L) diff --git a/src/test/java/org/ject/support/admin/apply/service/AdminApplyServiceTest.java b/src/test/java/org/ject/support/admin/apply/service/AdminApplyServiceTest.java index 5fe8c04c..d064a456 100644 --- a/src/test/java/org/ject/support/admin/apply/service/AdminApplyServiceTest.java +++ b/src/test/java/org/ject/support/admin/apply/service/AdminApplyServiceTest.java @@ -24,7 +24,7 @@ import org.ject.support.domain.apply.exception.ApplyException; import org.ject.support.domain.apply.repository.ApplyRepository; import org.ject.support.domain.member.JobFamily; -import org.ject.support.domain.member.entity.Member; +import org.ject.support.domain.applicant.entity.Applicant; import org.ject.support.domain.recruit.domain.Question; import org.ject.support.domain.recruit.domain.Recruit; import org.ject.support.domain.recruit.domain.RecruitType; @@ -60,7 +60,7 @@ class AdminApplyServiceTest extends UnitTestSupport { @BeforeEach void setUp() { var applyId = 1L; - var member = Member.builder() + var member = Applicant.builder() .name("김젝트") .phoneNumber("010-1234-5678") .email("test@mail.com") @@ -89,7 +89,7 @@ void setUp() { submittedApply = Apply.builder() .id(applyId) - .member(member) + .applicant(member) .recruit(recruit) .status(ApplyStatus.SUBMITTED) .note("Test note") @@ -101,14 +101,14 @@ void setUp() { void 단건_삭제_성공() { // given var applyId = submittedApply.getId(); - given(adminApplyRepository.findByIdWithMember(applyId)) + given(adminApplyRepository.findByIdWithApplicant(applyId)) .willReturn(Optional.of(submittedApply)); // when adminApplyService.deleteApply(applyId); // then - verify(adminApplyRepository).findByIdWithMember(applyId); + verify(adminApplyRepository).findByIdWithApplicant(applyId); verify(adminApplyRepository).delete(submittedApply); } @@ -116,7 +116,7 @@ void setUp() { void 단건_삭제시_존재하지_않으면_예외_발생() { // given var applyId = 999L; - given(adminApplyRepository.findByIdWithMember(applyId)) + given(adminApplyRepository.findByIdWithApplicant(applyId)) .willReturn(Optional.empty()); // expected @@ -211,10 +211,10 @@ void setUp() { var jobFamily = JobFamily.BE; Long semesterId = null; - var member2 = Member.builder().name("김젝트2").build(); + var member2 = Applicant.builder().name("김젝트2").build(); var apply2 = Apply.builder() .id(2L) - .member(member2) + .applicant(member2) .recruit(submittedApply.getRecruit()) .status(ApplyStatus.SUBMITTED) .applicationForm(ApplicationForm.builder().build()) @@ -299,14 +299,14 @@ void setUp() { void 상세조회하려는_제출된_지원서의_ApplicationForm이_null이면_빈_응답을_반환() { // given var applyId = submittedApply.getId() + 1L; - var member2 = Member.builder() + var member2 = Applicant.builder() .name("김젝트2") .build(); var apply2 = Apply.builder() .id(applyId) .status(ApplyStatus.SUBMITTED) .applicationForm(null) - .member(member2) + .applicant(member2) .recruit(submittedApply.getRecruit()) .build(); @@ -351,7 +351,7 @@ void setUp() { // given var tempApply = Apply.builder() .id(10L) - .member(submittedApply.getMember()) + .applicant(submittedApply.getApplicant()) .recruit(submittedApply.getRecruit()) .status(ApplyStatus.TEMP_SAVED) .note("") @@ -375,7 +375,7 @@ void setUp() { // given var rejectedApply = Apply.builder() .id(20L) - .member(submittedApply.getMember()) + .applicant(submittedApply.getApplicant()) .recruit(submittedApply.getRecruit()) .status(ApplyStatus.REJECTED) .note("") @@ -398,7 +398,7 @@ void setUp() { // given var joinedApply = Apply.builder() .id(30L) - .member(submittedApply.getMember()) + .applicant(submittedApply.getApplicant()) .recruit(submittedApply.getRecruit()) .status(ApplyStatus.JOINED) .note("") @@ -498,7 +498,7 @@ void setUp() { newPortfolios ); - given(adminApplyRepository.findByIdAndStatusWithMember(applyId, ApplyStatus.SUBMITTED)) + given(adminApplyRepository.findByIdAndStatusWithApplicant(applyId, ApplyStatus.SUBMITTED)) .willReturn(Optional.of(submittedApply)); given(map2JsonSerializer.serializeAsString(newAnswers)) .willReturn("{\"1\":\"수정된 답변1\",\"2\":\"수정된 답변2\"}"); @@ -507,12 +507,12 @@ void setUp() { adminApplyService.updateSubmittedApply(applyId, request); // then - verify(adminApplyRepository).findByIdAndStatusWithMember(applyId, ApplyStatus.SUBMITTED); + verify(adminApplyRepository).findByIdAndStatusWithApplicant(applyId, ApplyStatus.SUBMITTED); verify(map2JsonSerializer).serializeAsString(newAnswers); - assertThat(submittedApply.getMember().getName()).isEqualTo(newName); - assertThat(submittedApply.getMember().getPhoneNumber()).isEqualTo(newPhoneNumber); - assertThat(submittedApply.getMember().getEmail()).isEqualTo(newEmail); - assertThat(submittedApply.getMember().getJobFamily()).isEqualTo(newJobFamily); + assertThat(submittedApply.getApplicant().getName()).isEqualTo(newName); + assertThat(submittedApply.getApplicant().getPhoneNumber()).isEqualTo(newPhoneNumber); + assertThat(submittedApply.getApplicant().getEmail()).isEqualTo(newEmail); + assertThat(submittedApply.getApplicant().getJobFamily()).isEqualTo(newJobFamily); } @Test @@ -528,7 +528,7 @@ void setUp() { List.of() ); - given(adminApplyRepository.findByIdAndStatusWithMember(applyId, ApplyStatus.SUBMITTED)) + given(adminApplyRepository.findByIdAndStatusWithApplicant(applyId, ApplyStatus.SUBMITTED)) .willReturn(Optional.empty()); // expected @@ -552,7 +552,7 @@ void setUp() { List.of() ); - given(adminApplyRepository.findByIdAndStatusWithMember(applyId, ApplyStatus.SUBMITTED)) + given(adminApplyRepository.findByIdAndStatusWithApplicant(applyId, ApplyStatus.SUBMITTED)) .willReturn(Optional.of(submittedApply)); // expected @@ -587,8 +587,8 @@ void setUp() { var pageable = PageRequest.of(0, 10); Long semesterId = null; - var m1 = Member.builder().name("김1").jobFamily(JobFamily.BE).build(); - var m2 = Member.builder().name("김2").jobFamily(JobFamily.BE).build(); + var m1 = Applicant.builder().name("김1").jobFamily(JobFamily.BE).build(); + var m2 = Applicant.builder().name("김2").jobFamily(JobFamily.BE).build(); var semester = Semester.builder().name("1").build(); var recruit = Recruit.builder() .semester(semester) @@ -597,7 +597,7 @@ void setUp() { var a1 = Apply.builder() .id(1L) - .member(m1) + .applicant(m1) .recruit(recruit) .status(ApplyStatus.TEMP_SAVED) .applicationForm(ApplicationForm.builder().build()) @@ -605,7 +605,7 @@ void setUp() { var a2 = Apply.builder() .id(2L) - .member(m2) + .applicant(m2) .recruit(recruit) .status(ApplyStatus.TEMP_SAVED) .applicationForm(ApplicationForm.builder().build()) @@ -635,7 +635,7 @@ void setUp() { var pageable = PageRequest.of(0, 10); Long semesterId = 1L; - var member = Member.builder().name("김젝트").jobFamily(JobFamily.BE).build(); + var member = Applicant.builder().name("김젝트").jobFamily(JobFamily.BE).build(); var semester = Semester.builder().name("1").build(); var recruit = Recruit.builder() .semester(semester) @@ -644,7 +644,7 @@ void setUp() { var apply = Apply.builder() .id(1L) - .member(member) + .applicant(member) .recruit(recruit) .status(ApplyStatus.TEMP_SAVED) .applicationForm(ApplicationForm.builder().build()) diff --git a/src/test/java/org/ject/support/admin/apply/service/ApplyPassServiceTest.java b/src/test/java/org/ject/support/admin/apply/service/ApplyPassServiceTest.java index c6ade8e8..27fcaeee 100644 --- a/src/test/java/org/ject/support/admin/apply/service/ApplyPassServiceTest.java +++ b/src/test/java/org/ject/support/admin/apply/service/ApplyPassServiceTest.java @@ -10,7 +10,7 @@ import org.ject.support.domain.member.MemberStatus; import org.ject.support.domain.member.MemberType; import org.ject.support.domain.member.Role; -import org.ject.support.domain.member.entity.Member; +import org.ject.support.domain.applicant.entity.Applicant; import org.ject.support.domain.recruit.domain.Question; import org.ject.support.domain.recruit.domain.Recruit; import org.ject.support.domain.recruit.domain.RecruitType; @@ -39,15 +39,15 @@ class ApplyPassServiceTest extends UnitTestSupport { // given Recruit recruit = getActiveRecruit(1L, JobFamily.BE, List.of()); - Member applicant1 = getApplicant(1L, "applicant1@test.com"); - Member applicant2 = getApplicant(2L, "applicant2@test.com"); - Member applicant3 = getApplicant(3L, "applicant3@test.com"); + Applicant applicant1 = getApplicant(1L, "applicant1@test.com"); + Applicant applicant2 = getApplicant(2L, "applicant2@test.com"); + Applicant applicant3 = getApplicant(3L, "applicant3@test.com"); Apply apply1 = getApply(1L, recruit, applicant1, getApplicationForm("content"), ApplyStatus.SUBMITTED); Apply apply2 = getApply(2L, recruit, applicant2, getApplicationForm("content"), ApplyStatus.SUBMITTED); Apply apply3 = getApply(3L, recruit, applicant3, getApplicationForm("content"), ApplyStatus.SUBMITTED); - when(applyRepository.findAllByIdWithMember(List.of(1L, 2L, 3L))).thenReturn(List.of(apply1, apply2, apply3)); + when(applyRepository.findAllByIdWithApplicant(List.of(1L, 2L, 3L))).thenReturn(List.of(apply1, apply2, apply3)); // when int result = applyPassService.passApply(List.of(1L, 2L, 3L)); @@ -66,10 +66,10 @@ class ApplyPassServiceTest extends UnitTestSupport { void 메이커스_지원자_합격_시_구성원_타입을_메이커스로_변경한다() { // given Recruit recruit = getActiveRecruit(1L, JobFamily.FE, RecruitType.MAKERS, List.of()); - Member applicant = getApplicant(1L, "applicant@test.com"); + Applicant applicant = getApplicant(1L, "applicant@test.com"); Apply apply = getApply(1L, recruit, applicant, getApplicationForm("content"), ApplyStatus.SUBMITTED); - when(applyRepository.findAllByIdWithMember(List.of(1L))).thenReturn(List.of(apply)); + when(applyRepository.findAllByIdWithApplicant(List.of(1L))).thenReturn(List.of(apply)); // when int result = applyPassService.passApply(List.of(1L)); @@ -84,10 +84,10 @@ class ApplyPassServiceTest extends UnitTestSupport { void 운영_서포터즈_지원자_합격_시_구성원_타입을_운영_서포터즈로_변경한다() { // given Recruit recruit = getActiveRecruit(1L, JobFamily.SUPPORTER, RecruitType.SUPPORTERS, List.of()); - Member applicant = getApplicant(1L, "applicant@test.com"); + Applicant applicant = getApplicant(1L, "applicant@test.com"); Apply apply = getApply(1L, recruit, applicant, getApplicationForm("content"), ApplyStatus.SUBMITTED); - when(applyRepository.findAllByIdWithMember(List.of(1L))).thenReturn(List.of(apply)); + when(applyRepository.findAllByIdWithApplicant(List.of(1L))).thenReturn(List.of(apply)); // when int result = applyPassService.passApply(List.of(1L)); @@ -102,15 +102,15 @@ class ApplyPassServiceTest extends UnitTestSupport { void 제출되지_않은_지원서_승인_실패() { Recruit recruit = getActiveRecruit(1L, JobFamily.BE, List.of()); - Member applicant1 = getApplicant(1L, "applicant1@test.com"); - Member applicant2 = getApplicant(2L, "applicant2@test.com"); - Member applicant3 = getApplicant(3L, "applicant3@test.com"); + Applicant applicant1 = getApplicant(1L, "applicant1@test.com"); + Applicant applicant2 = getApplicant(2L, "applicant2@test.com"); + Applicant applicant3 = getApplicant(3L, "applicant3@test.com"); Apply apply1 = getApply(1L, recruit, applicant1, getApplicationForm("content"), ApplyStatus.SUBMITTED); Apply apply2 = getApply(2L, recruit, applicant2, getApplicationForm("content"), ApplyStatus.TEMP_SAVED); Apply apply3 = getApply(3L, recruit, applicant3, getApplicationForm("content"), ApplyStatus.SUBMITTED); - when(applyRepository.findAllByIdWithMember(List.of(1L, 2L, 3L))).thenReturn(List.of(apply1, apply2, apply3)); + when(applyRepository.findAllByIdWithApplicant(List.of(1L, 2L, 3L))).thenReturn(List.of(apply1, apply2, apply3)); // when, then assertThatThrownBy(() -> applyPassService.passApply(List.of(1L, 2L, 3L))) @@ -133,8 +133,8 @@ private Recruit getActiveRecruit(Long id, JobFamily jobFamily, RecruitType recru .build(); } - private Member getApplicant(Long id, String email) { - return Member.builder() + private Applicant getApplicant(Long id, String email) { + return Applicant.builder() .id(id) .email(email) .pin("111111") @@ -143,11 +143,11 @@ private Member getApplicant(Long id, String email) { .build(); } - private Apply getApply(Long id, Recruit recruit, Member applicant, ApplicationForm applicationForm, ApplyStatus status) { + private Apply getApply(Long id, Recruit recruit, Applicant applicant, ApplicationForm applicationForm, ApplyStatus status) { return Apply.builder() .id(id) .recruit(recruit) - .member(applicant) + .applicant(applicant) .applicationForm(applicationForm) .status(status) .build(); diff --git a/src/test/java/org/ject/support/domain/applicant/repository/ApplicantRepositoryTest.java b/src/test/java/org/ject/support/domain/applicant/repository/ApplicantRepositoryTest.java index 00494a15..5e9458e9 100644 --- a/src/test/java/org/ject/support/domain/applicant/repository/ApplicantRepositoryTest.java +++ b/src/test/java/org/ject/support/domain/applicant/repository/ApplicantRepositoryTest.java @@ -1,19 +1,41 @@ package org.ject.support.domain.applicant.repository; +import org.ject.support.admin.account.dto.AdminAccountSearchCondition; +import org.ject.support.domain.apply.domain.ApplicationForm; +import org.ject.support.domain.apply.domain.Apply; +import org.ject.support.domain.apply.domain.ApplyStatus; +import org.ject.support.domain.apply.repository.ApplicationFormRepository; +import org.ject.support.domain.apply.repository.ApplyRepository; +import org.ject.support.domain.applicant.dto.ApplicantAccountProjection; import org.ject.support.domain.applicant.entity.Applicant; import org.ject.support.domain.member.JobFamily; import org.ject.support.domain.member.MemberStatus; import org.ject.support.domain.member.Role; +import org.ject.support.domain.recruit.domain.Recruit; +import org.ject.support.domain.recruit.domain.Semester; +import org.ject.support.domain.recruit.repository.RecruitRepository; +import org.ject.support.domain.recruit.repository.SemesterRepository; import org.ject.support.testconfig.QueryDslTestConfig; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest; import org.springframework.context.annotation.Import; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.PageRequest; +import org.springframework.test.util.ReflectionTestUtils; +import java.time.LocalDateTime; import java.util.List; import java.util.Optional; -import static org.assertj.core.api.AssertionsForClassTypes.assertThat; +import static org.assertj.core.api.Assertions.assertThat; +import static org.ject.support.domain.apply.domain.ApplyStatus.JOINED; +import static org.ject.support.domain.apply.domain.ApplyStatus.SUBMITTED; +import static org.ject.support.domain.apply.domain.ApplyStatus.TEMP_SAVED; +import static org.ject.support.domain.member.JobFamily.BE; +import static org.ject.support.domain.member.JobFamily.FE; +import static org.ject.support.domain.member.JobFamily.PD; +import static org.ject.support.domain.member.JobFamily.PM; @Import(QueryDslTestConfig.class) @DataJpaTest @@ -22,6 +44,18 @@ class ApplicantRepositoryTest { @Autowired ApplicantRepository applicantRepository; + @Autowired + ApplyRepository applyRepository; + + @Autowired + ApplicationFormRepository applicationFormRepository; + + @Autowired + RecruitRepository recruitRepository; + + @Autowired + SemesterRepository semesterRepository; + @Test void 이메일과_역활로_지원자를_조회시_존재하는_지원자를_반환한다() { // given @@ -122,6 +156,122 @@ class ApplicantRepositoryTest { assertThat(found).isEmpty(); } + @Test + void 전달_받은_ID_중_지원서를_제출하지_않은_지원자의_이메일_목록_조회() { + // given + Applicant be5 = createApplicant("이젝트", "01011112231", "be5@test.kr", BE, Role.APPLY); + Applicant be6 = createApplicant("박젝트", "01011112232", "be6@test.kr", BE, Role.APPLY); + Applicant pm7 = createApplicant("서젝트", "01011112233", "pm7@test.kr", PM, Role.APPLY); + Applicant fe8 = createApplicant("양젝트", "01011112234", "fe8@test.kr", FE, Role.APPLY); + Applicant be9 = createApplicant("조젝트", "01011112235", "be9@test.kr", BE, Role.APPLY); + Applicant pd10 = createApplicant("표젝트", "01011112236", "pd10@test.kr", PD, Role.APPLY); + List testApplicants = applicantRepository.saveAll(List.of(be5, be6, pm7, fe8, be9, pd10)); + + Semester savedSemester = semesterRepository.save(Semester.builder() + .name("1기") + .isRecruiting(true) + .build()); + Recruit pmRecruit = createRecruit(savedSemester, PM); + Recruit pdRecruit = createRecruit(savedSemester, PD); + Recruit feRecruit = createRecruit(savedSemester, FE); + Recruit beRecruit = createRecruit(savedSemester, BE); + recruitRepository.saveAll(List.of(pmRecruit, pdRecruit, feRecruit, beRecruit)); + + // 일부 지원자만 지원서 제출 + Apply be5Apply = applyRepository.save(createApply(beRecruit, be5, SUBMITTED)); + applyRepository.save(createApply(beRecruit, be6, JOINED)); + Apply pm7Apply = applyRepository.save(createApply(pmRecruit, pm7, SUBMITTED)); + Apply fe8Apply = applyRepository.save(createApply(feRecruit, fe8, SUBMITTED)); + applyRepository.save(createApply(beRecruit, be9, JOINED)); + applyRepository.save(createApply(pdRecruit, pd10, TEMP_SAVED)); + + applicationFormRepository.saveAll(List.of( + createApplicationForm(be5Apply), + createApplicationForm(pm7Apply), + createApplicationForm(fe8Apply))); + + List applicantIds = testApplicants.stream().map(Applicant::getId).toList(); + + // when + List result = applicantRepository.findEmailsByIdsAndNotSubmitted(applicantIds); + + // then + assertThat(result).asList() + .hasSize(3) + .containsExactlyInAnyOrder(be6.getEmail(), be9.getEmail(), pd10.getEmail()); + } + + @Test + void 필터가_없으면_삭제되지_않은_백오피스_계정만_조회한다() { + // given + Applicant admin = createApplicant("관리자", "01011112221", "admin@ject.kr", BE, Role.ADMIN); + Applicant operations = createApplicant("운영자", "01011112222", "operations@ject.kr", BE, Role.OPERATIONS); + Applicant semester = createApplicant("일반회원", "01011112223", "semester@ject.kr", BE, Role.SEMESTER); + Applicant deleted = createApplicant("삭제회원", "01011112224", "deleted@ject.kr", BE, Role.SUPPORTER); + ReflectionTestUtils.setField(deleted, "isDeleted", true); + applicantRepository.saveAll(List.of(admin, operations, semester, deleted)); + + // when + Page result = applicantRepository.findAccounts( + new AdminAccountSearchCondition(null, null), + PageRequest.of(0, 20)); + + // then + assertThat(result.getTotalElements()).isEqualTo(2); + assertThat(result.getContent()) + .extracting(ApplicantAccountProjection::email) + .containsExactlyInAnyOrder("admin@ject.kr", "operations@ject.kr"); + } + + @Test + void 계정_유형과_상태를_복수_필터로_조회한다() { + // given + Applicant activeAdmin = createApplicant("활성관리자", "01011112225", "active-admin@ject.kr", BE, Role.ADMIN); + Applicant lockedAdmin = createApplicant("잠긴관리자", "01011112226", "locked-admin@ject.kr", BE, Role.ADMIN); + Applicant activeSupporter = createApplicant("활성서포터", "01011112227", "active-supporter@ject.kr", BE, Role.SUPPORTER); + Applicant lockedOperations = createApplicant("잠긴운영자", "01011112228", "locked-operations@ject.kr", BE, Role.OPERATIONS); + ReflectionTestUtils.setField(lockedAdmin, "status", MemberStatus.LOCKED); + ReflectionTestUtils.setField(lockedOperations, "status", MemberStatus.LOCKED); + applicantRepository.saveAll(List.of(activeAdmin, lockedAdmin, activeSupporter, lockedOperations)); + + AdminAccountSearchCondition condition = new AdminAccountSearchCondition( + List.of(Role.ADMIN, Role.SUPPORTER), + List.of(MemberStatus.ACTIVE)); + + // when + Page result = applicantRepository.findAccounts(condition, PageRequest.of(0, 20)); + + // then + assertThat(result.getTotalElements()).isEqualTo(2); + assertThat(result.getContent()) + .extracting(ApplicantAccountProjection::email) + .containsExactlyInAnyOrder("active-admin@ject.kr", "active-supporter@ject.kr"); + } + + @Test + void 페이지네이션과_기본_정렬을_적용한다() { + // given + Applicant first = createApplicant("첫번째", "01011112229", "first@ject.kr", BE, Role.ADMIN); + Applicant second = createApplicant("두번째", "01011112230", "second@ject.kr", BE, Role.ADMIN); + Applicant third = createApplicant("세번째", "01011112234", "third@ject.kr", BE, Role.ADMIN); + setCreatedAt(first, LocalDateTime.of(2026, 1, 1, 0, 0)); + setCreatedAt(second, LocalDateTime.of(2026, 1, 2, 0, 0)); + setCreatedAt(third, LocalDateTime.of(2026, 1, 3, 0, 0)); + applicantRepository.saveAll(List.of(first, second, third)); + applicantRepository.flush(); + + // when + Page result = applicantRepository.findAccounts( + new AdminAccountSearchCondition(null, null), + PageRequest.of(0, 2)); + + // then + assertThat(result.getTotalElements()).isEqualTo(3); + assertThat(result.getContent()) + .extracting(ApplicantAccountProjection::email) + .containsExactly("third@ject.kr", "second@ject.kr"); + } + private Applicant createApplicant(String name, String phoneNumber, String email, JobFamily jobFamily, Role role) { return Applicant.builder() .name(name) @@ -134,4 +284,32 @@ private Applicant createApplicant(String name, String phoneNumber, String email, .status(MemberStatus.ACTIVE) .build(); } + + private Recruit createRecruit(Semester semester, JobFamily jobFamily) { + return Recruit.builder() + .semester(semester) + .jobFamily(jobFamily) + .startDate(java.time.LocalDateTime.now().minusDays(1)) + .endDate(java.time.LocalDateTime.now().plusDays(1)) + .build(); + } + + private Apply createApply(Recruit recruit, Applicant applicant, ApplyStatus status) { + return Apply.builder() + .applicant(applicant) + .recruit(recruit) + .status(status) + .build(); + } + + private ApplicationForm createApplicationForm(Apply apply) { + return ApplicationForm.builder() + .content("content") + .apply(apply) + .build(); + } + + private void setCreatedAt(Applicant applicant, LocalDateTime createdAt) { + ReflectionTestUtils.setField(applicant, "createdAt", createdAt); + } } diff --git a/src/test/java/org/ject/support/domain/apply/domain/ApplyTest.java b/src/test/java/org/ject/support/domain/apply/domain/ApplyTest.java index 2ce99087..fe71e39a 100644 --- a/src/test/java/org/ject/support/domain/apply/domain/ApplyTest.java +++ b/src/test/java/org/ject/support/domain/apply/domain/ApplyTest.java @@ -1,10 +1,10 @@ package org.ject.support.domain.apply.domain; import org.ject.support.base.TestSupport; +import org.ject.support.domain.applicant.entity.Applicant; import org.ject.support.domain.apply.exception.ApplyErrorCode; import org.ject.support.domain.apply.exception.ApplyException; import org.ject.support.domain.member.JobFamily; -import org.ject.support.domain.member.entity.Member; import org.ject.support.domain.recruit.domain.Recruit; import org.junit.jupiter.api.Test; @@ -22,7 +22,7 @@ class ApplyTest extends TestSupport { Recruit recruit = Recruit.builder() .jobFamily(JobFamily.BE) .build(); - Apply apply = Apply.createApply(Member.builder().build(), recruit); + Apply apply = Apply.createApply(Applicant.builder().build(), recruit); ApplicationForm form = ApplicationForm.builder().build(); // when @@ -39,7 +39,7 @@ class ApplyTest extends TestSupport { Recruit recruit = Recruit.builder() .jobFamily(JobFamily.PD) .build(); - Apply apply = Apply.createApply(Member.builder().build(), recruit); + Apply apply = Apply.createApply(Applicant.builder().build(), recruit); Portfolio portfolio = Portfolio.builder().build(); ApplicationForm form = ApplicationForm.builder() @@ -60,7 +60,7 @@ class ApplyTest extends TestSupport { Recruit recruit = Recruit.builder() .jobFamily(JobFamily.PD) .build(); - Apply apply = Apply.createApply(Member.builder().build(), recruit); + Apply apply = Apply.createApply(Applicant.builder().build(), recruit); ApplicationForm form = ApplicationForm.builder().build(); // 빈 포트폴리오 // when & then @@ -76,7 +76,7 @@ class ApplyTest extends TestSupport { Recruit recruit = Recruit.builder() .jobFamily(JobFamily.BE) .build(); - Apply apply = Apply.createApply(Member.builder().build(), recruit); + Apply apply = Apply.createApply(Applicant.builder().build(), recruit); apply.updateStatus(SUBMITTED); // 이미 제출된 상태 ApplicationForm form = ApplicationForm.builder().build(); diff --git a/src/test/java/org/ject/support/domain/apply/repository/ApplyRepositoryTest.java b/src/test/java/org/ject/support/domain/apply/repository/ApplyRepositoryTest.java index a5de7925..976677d4 100644 --- a/src/test/java/org/ject/support/domain/apply/repository/ApplyRepositoryTest.java +++ b/src/test/java/org/ject/support/domain/apply/repository/ApplyRepositoryTest.java @@ -7,8 +7,8 @@ import org.ject.support.domain.member.JobFamily; import org.ject.support.domain.member.MemberStatus; import org.ject.support.domain.member.Role; -import org.ject.support.domain.member.entity.Member; -import org.ject.support.domain.member.repository.MemberRepository; +import org.ject.support.domain.applicant.entity.Applicant; +import org.ject.support.domain.applicant.repository.ApplicantRepository; import org.ject.support.domain.recruit.domain.Recruit; import org.ject.support.domain.recruit.domain.Semester; import org.ject.support.domain.recruit.repository.RecruitRepository; @@ -55,7 +55,7 @@ class ApplyRepositoryTest { Recruit beRecruit; @Autowired - private MemberRepository memberRepository; + private ApplicantRepository applicantRepository; @BeforeEach void setUp() { @@ -73,11 +73,11 @@ void setUp() { @Test void 모집과_지원상태를_바탕으로_임시저장_상태의_지원정보_조회_성공() { // given - Member feApplicant = createMember("emailFE@test.com", Role.APPLY); - Member be1Applicant = createMember("emailBE1@test.com", Role.APPLY); - Member be2Applicant = createMember("emailBE2@test.com", Role.APPLY); - Member be3Applicant = createMember("emailBE3@test.com", Role.APPLY); - memberRepository.saveAll(List.of(feApplicant, be1Applicant, be2Applicant, be3Applicant)); + Applicant feApplicant = createApplicant("emailFE@test.com", Role.APPLY); + Applicant be1Applicant = createApplicant("emailBE1@test.com", Role.APPLY); + Applicant be2Applicant = createApplicant("emailBE2@test.com", Role.APPLY); + Applicant be3Applicant = createApplicant("emailBE3@test.com", Role.APPLY); + applicantRepository.saveAll(List.of(feApplicant, be1Applicant, be2Applicant, be3Applicant)); Apply feTempApply = getApply(feApplicant, feRecruit, TEMP_SAVED); Apply be1SubmitApply = getApply(be1Applicant, beRecruit, SUBMITTED); @@ -97,9 +97,9 @@ void setUp() { @Test void 상태별_지원서_수_조회_성공() { // given - Member feApplicant = createMember("emailFE@test.com", Role.APPLY); - Member beApplicant = createMember("emailBE@test.com", Role.APPLY); - memberRepository.saveAll(List.of(feApplicant, beApplicant)); + Applicant feApplicant = createApplicant("emailFE@test.com", Role.APPLY); + Applicant beApplicant = createApplicant("emailBE@test.com", Role.APPLY); + applicantRepository.saveAll(List.of(feApplicant, beApplicant)); Apply feTempApply = getApply(feApplicant, feRecruit, TEMP_SAVED); Apply beSubmitApply = getApply(beApplicant, beRecruit, SUBMITTED); @@ -115,17 +115,17 @@ void setUp() { @Test void 회원과_모집_공고를_바탕으로_활성_지원정보_존재_여부를_조회한다() { // given - Member applicant = createMember("email@test.com", Role.APPLY); - memberRepository.save(applicant); + Applicant applicant = createApplicant("email@test.com", Role.APPLY); + applicantRepository.save(applicant); Apply feApply = getApply(applicant, feRecruit, JOINED); Apply beApply = getApply(applicant, beRecruit, JOINED); applyRepository.saveAll(List.of(feApply, beApply)); // when - boolean exists = applyRepository.existsByMemberIdAndRecruitIdInActiveRecruit( + boolean exists = applyRepository.existsByApplicantIdAndRecruitIdInActiveRecruit( applicant.getId(), beRecruit.getId(), LocalDateTime.now()); - boolean notExists = applyRepository.existsByMemberIdAndRecruitIdInActiveRecruit( + boolean notExists = applyRepository.existsByApplicantIdAndRecruitIdInActiveRecruit( applicant.getId(), pdRecruit.getId(), LocalDateTime.now()); // then @@ -136,15 +136,15 @@ void setUp() { @Test void 회원과_모집_공고를_바탕으로_활성_지원정보를_조회한다() { // given - Member applicant = createMember("email@test.com", Role.APPLY); - memberRepository.save(applicant); + Applicant applicant = createApplicant("email@test.com", Role.APPLY); + applicantRepository.save(applicant); Apply feApply = getApply(applicant, feRecruit, JOINED); Apply beApply = getApply(applicant, beRecruit, TEMP_SAVED); applyRepository.saveAll(List.of(feApply, beApply)); // when - Apply result = applyRepository.findByMemberIdAndRecruitIdInActiveRecruit( + Apply result = applyRepository.findByApplicantIdAndRecruitIdInActiveRecruit( applicant.getId(), beRecruit.getId(), LocalDateTime.now()).orElseThrow(); // then @@ -155,21 +155,21 @@ void setUp() { @Test void 지원ID와_지원서의_상태로_지원서를_상세_조회한다() { // given - Member feApplicant = createMember("emailFE@test.com", Role.APPLY); - Member be1Applicant = createMember("emailBE1@test.com", Role.APPLY); - memberRepository.saveAll(List.of(feApplicant, be1Applicant)); + Applicant feApplicant = createApplicant("emailFE@test.com", Role.APPLY); + Applicant be1Applicant = createApplicant("emailBE1@test.com", Role.APPLY); + applicantRepository.saveAll(List.of(feApplicant, be1Applicant)); Apply feTempApply = getApply(feApplicant, feRecruit, TEMP_SAVED); Apply be1SubmitApply = getApply(be1Applicant, beRecruit, TEMP_SAVED); applyRepository.saveAll(List.of(feTempApply, be1SubmitApply)); // when - Apply result = applyRepository.findByIdAndStatusWithMember(feTempApply.getId(), TEMP_SAVED) + Apply result = applyRepository.findByIdAndStatusWithApplicant(feTempApply.getId(), TEMP_SAVED) .orElseThrow(); // then assertThat(result).isEqualTo(feTempApply); - assertThat(result.getMember()).isEqualTo(feApplicant); + assertThat(result.getApplicant()).isEqualTo(feApplicant); assertThat(result.getRecruit()).isEqualTo(feRecruit); assertThat(result.getStatus()).isEqualTo(TEMP_SAVED); } @@ -177,18 +177,18 @@ void setUp() { @Test void 지원ID_목록으로_회원과_모집을_함께_조회한다() { // given - Member applicant = createMember("email@test.com", Role.APPLY); - memberRepository.save(applicant); + Applicant applicant = createApplicant("email@test.com", Role.APPLY); + applicantRepository.save(applicant); Apply savedApply = applyRepository.save(getApply(applicant, beRecruit, SUBMITTED)); entityManager.flush(); entityManager.clear(); // when - Apply result = applyRepository.findAllByIdWithMember(List.of(savedApply.getId())).get(0); + Apply result = applyRepository.findAllByIdWithApplicant(List.of(savedApply.getId())).get(0); // then PersistenceUnitUtil persistenceUnitUtil = entityManager.getEntityManagerFactory().getPersistenceUnitUtil(); - assertThat(persistenceUnitUtil.isLoaded(result.getMember())).isTrue(); + assertThat(persistenceUnitUtil.isLoaded(result.getApplicant())).isTrue(); assertThat(persistenceUnitUtil.isLoaded(result.getRecruit())).isTrue(); } @@ -201,16 +201,16 @@ private Recruit getRecruit(JobFamily jobFamily) { .build(); } - private Apply getApply(Member member, Recruit recruit, ApplyStatus status) { + private Apply getApply(Applicant applicant, Recruit recruit, ApplyStatus status) { return Apply.builder() .recruit(recruit) - .member(member) + .applicant(applicant) .status(status) .build(); } - private Member createMember(String email, Role role) { - return Member.builder() + private Applicant createApplicant(String email, Role role) { + return Applicant.builder() .email(email) .semesterId(1L) .role(role) diff --git a/src/test/java/org/ject/support/domain/apply/service/ApplyServiceTest.java b/src/test/java/org/ject/support/domain/apply/service/ApplyServiceTest.java index 967d3455..a63eba22 100644 --- a/src/test/java/org/ject/support/domain/apply/service/ApplyServiceTest.java +++ b/src/test/java/org/ject/support/domain/apply/service/ApplyServiceTest.java @@ -21,8 +21,8 @@ import org.ject.support.domain.member.MemberStatus; import org.ject.support.domain.member.Region; import org.ject.support.domain.member.Role; -import org.ject.support.domain.member.entity.Member; -import org.ject.support.domain.member.repository.MemberRepository; +import org.ject.support.domain.applicant.entity.Applicant; +import org.ject.support.domain.applicant.repository.ApplicantRepository; import org.ject.support.domain.recruit.domain.Question; import org.ject.support.domain.recruit.domain.Recruit; import org.ject.support.domain.recruit.domain.Semester; @@ -73,7 +73,7 @@ class ApplyServiceTest extends UnitTestSupport { ApplicationFormRepository applicationFormRepository; @Mock - MemberRepository memberRepository; + ApplicantRepository applicantRepository; @Mock String2MapSerializer string2MapSerializer; @@ -92,7 +92,7 @@ class ApplyServiceTest extends UnitTestSupport { Recruit recruit = getActiveRecruit(BE, questions); - Member applicant = getApplicant(1L, "email@test.com"); + Applicant applicant = getApplicant(1L, "email@test.com"); Map answers = Map.of( "1", "답변 1", @@ -104,7 +104,7 @@ class ApplyServiceTest extends UnitTestSupport { Apply apply = getApply(1L, recruit, applicant, applicationForm, TEMP_SAVED); - when(applyRepository.findByMemberIdAndRecruitIdInActiveRecruit( + when(applyRepository.findByApplicantIdAndRecruitIdInActiveRecruit( eq(applicant.getId()), eq(recruit.getId()), any())).thenReturn(Optional.of(apply)); when(map2JsonSerializer.serializeAsString(answers)).thenReturn(answers.toString()); @@ -130,12 +130,12 @@ class ApplyServiceTest extends UnitTestSupport { getQuestion(1L, 1, "문항 1", "설명 1")); Recruit recruit = getActiveRecruit(PD, questions); - Member applicant = getApplicant(1L, "email@test.com"); + Applicant applicant = getApplicant(1L, "email@test.com"); Map answers = Map.of("1", "답변 1"); ApplicationForm applicationForm = getApplicationForm(answers.toString()); Apply apply = getApply(1L, recruit, applicant, applicationForm, TEMP_SAVED); - when(applyRepository.findByMemberIdAndRecruitIdInActiveRecruit( + when(applyRepository.findByApplicantIdAndRecruitIdInActiveRecruit( eq(applicant.getId()), eq(recruit.getId()), any())).thenReturn(Optional.of(apply)); when(map2JsonSerializer.serializeAsString(answers)).thenReturn(answers.toString()); @@ -153,7 +153,7 @@ class ApplyServiceTest extends UnitTestSupport { getQuestion(1L, 1, "문항 1", "설명 1")); Recruit recruit = getActiveRecruit(PD, questions); - Member applicant = getApplicant(1L, "email@test.com"); + Applicant applicant = getApplicant(1L, "email@test.com"); Map answers = Map.of("1", "답변 1"); ApplicationForm applicationForm = getApplicationForm(answers.toString()); Apply apply = getApply(1L, recruit, applicant, applicationForm, TEMP_SAVED); @@ -162,7 +162,7 @@ class ApplyServiceTest extends UnitTestSupport { new ApplyPortfolioDto("url", "name", "100", "1") ); - when(applyRepository.findByMemberIdAndRecruitIdInActiveRecruit( + when(applyRepository.findByApplicantIdAndRecruitIdInActiveRecruit( eq(applicant.getId()), eq(recruit.getId()), any())).thenReturn(Optional.of(apply)); when(map2JsonSerializer.serializeAsString(answers)).thenReturn(answers.toString()); @@ -191,9 +191,9 @@ class ApplyServiceTest extends UnitTestSupport { "3", "답변 3", "4", "답변 4"); - Member applicant = getApplicant(1L, "email@test.com"); + Applicant applicant = getApplicant(1L, "email@test.com"); Apply apply = getApply(1L, recruit, applicant, null, JOINED); - when(applyRepository.findByMemberIdAndRecruitIdInActiveRecruit( + when(applyRepository.findByApplicantIdAndRecruitIdInActiveRecruit( eq(applicant.getId()), eq(recruit.getId()), any())).thenReturn(Optional.of(apply)); // when, then @@ -205,18 +205,18 @@ class ApplyServiceTest extends UnitTestSupport { void 지원상태_조회_시_프로필작성을_하지_않았을_경우_예외발생() { // given Long recruitId = 1L; - Member member = Member.builder() + Applicant applicant = Applicant.builder() .id(1L) .name("지원자명") .phoneNumber("01012345678") .build(); - given(memberRepository.findById(member.getId())) - .willReturn(Optional.of(member)); - given(applyRepository.findByMemberIdAndRecruitIdInActiveRecruit(eq(member.getId()), eq(recruitId), any())) + given(applicantRepository.findById(applicant.getId())) + .willReturn(Optional.of(applicant)); + given(applyRepository.findByApplicantIdAndRecruitIdInActiveRecruit(eq(applicant.getId()), eq(recruitId), any())) .willReturn(Optional.empty()); // expected - assertThatThrownBy(() -> applyService.checkApplyStatus(member.getId(), recruitId)) + assertThatThrownBy(() -> applyService.checkApplyStatus(applicant.getId(), recruitId)) .isInstanceOf(ApplyException.class) .extracting("errorCode") .isEqualTo(ApplyErrorCode.NOT_FOUND_APPLY); @@ -226,14 +226,14 @@ class ApplyServiceTest extends UnitTestSupport { void 작성_중인_지원서가_있는_경우_TEMP_SAVED_반환() { // given Long recruitId = 1L; - Member member = Member.builder() + Applicant applicant = Applicant.builder() .id(1L) .name("지원자명") .phoneNumber("01012345678") .build(); - given(memberRepository.findById(member.getId())) - .willReturn(Optional.of(member)); - given(applyRepository.findByMemberIdAndRecruitIdInActiveRecruit(eq(member.getId()), eq(recruitId), any())) + given(applicantRepository.findById(applicant.getId())) + .willReturn(Optional.of(applicant)); + given(applyRepository.findByApplicantIdAndRecruitIdInActiveRecruit(eq(applicant.getId()), eq(recruitId), any())) .willReturn(Optional.of( Apply.builder() .id(1L) @@ -242,7 +242,7 @@ class ApplyServiceTest extends UnitTestSupport { )); // when - ApplyStatusResponse result = applyService.checkApplyStatus(member.getId(), recruitId); + ApplyStatusResponse result = applyService.checkApplyStatus(applicant.getId(), recruitId); // then assertThat(result.status()).isEqualTo(TEMP_SAVED); @@ -252,14 +252,14 @@ class ApplyServiceTest extends UnitTestSupport { void 지원서를_제출한_지원자에_대한_제출_상태_확인_시_SUBMITTED_반환() { // given Long recruitId = 1L; - Member member = Member.builder() + Applicant applicant = Applicant.builder() .id(1L) .name("지원자명") .phoneNumber("01012345678") .build(); - given(memberRepository.findById(member.getId())) - .willReturn(Optional.of(member)); - given(applyRepository.findByMemberIdAndRecruitIdInActiveRecruit(eq(member.getId()), eq(recruitId), any())) + given(applicantRepository.findById(applicant.getId())) + .willReturn(Optional.of(applicant)); + given(applyRepository.findByApplicantIdAndRecruitIdInActiveRecruit(eq(applicant.getId()), eq(recruitId), any())) .willReturn(Optional.of( Apply.builder() .id(1L) @@ -268,7 +268,7 @@ class ApplyServiceTest extends UnitTestSupport { )); // when - ApplyStatusResponse result = applyService.checkApplyStatus(member.getId(), recruitId); + ApplyStatusResponse result = applyService.checkApplyStatus(applicant.getId(), recruitId); // then assertThat(result.status()).isEqualTo(SUBMITTED); @@ -278,18 +278,18 @@ class ApplyServiceTest extends UnitTestSupport { void 동일_회원이라도_다른_공고ID로_조회하면_지원상태_조회_실패() { // given Long requestedRecruitId = 2L; - Member member = Member.builder() + Applicant applicant = Applicant.builder() .id(1L) .name("지원자명") .phoneNumber("01012345678") .build(); - given(memberRepository.findById(member.getId())) - .willReturn(Optional.of(member)); - given(applyRepository.findByMemberIdAndRecruitIdInActiveRecruit(eq(member.getId()), eq(requestedRecruitId), any())) + given(applicantRepository.findById(applicant.getId())) + .willReturn(Optional.of(applicant)); + given(applyRepository.findByApplicantIdAndRecruitIdInActiveRecruit(eq(applicant.getId()), eq(requestedRecruitId), any())) .willReturn(Optional.empty()); // expected - assertThatThrownBy(() -> applyService.checkApplyStatus(member.getId(), requestedRecruitId)) + assertThatThrownBy(() -> applyService.checkApplyStatus(applicant.getId(), requestedRecruitId)) .isInstanceOf(ApplyException.class) .extracting("errorCode") .isEqualTo(ApplyErrorCode.NOT_FOUND_APPLY); @@ -312,7 +312,7 @@ class ApplyServiceTest extends UnitTestSupport { "3", "답변 3", "4", "답변 4"); - Member applicant = getApplicant(1L, "email@test.com"); + Applicant applicant = getApplicant(1L, "email@test.com"); ApplicationForm applicationForm = getApplicationForm(answers.toString()); @@ -320,7 +320,7 @@ class ApplyServiceTest extends UnitTestSupport { String content = "newContent"; - when(applyRepository.findByMemberIdAndRecruitIdInActiveRecruit(eq(applicant.getId()), eq(recruit.getId()), any())) + when(applyRepository.findByApplicantIdAndRecruitIdInActiveRecruit(eq(applicant.getId()), eq(recruit.getId()), any())) .thenReturn(Optional.of(apply)); when(map2JsonSerializer.serializeAsString(answers)).thenReturn(content); @@ -356,13 +356,13 @@ class ApplyServiceTest extends UnitTestSupport { "3", "답변 3", "4", "답변 4"); - Member applicant = getApplicant(1L, "email@test.com"); + Applicant applicant = getApplicant(1L, "email@test.com"); ApplicationForm applicationForm = getApplicationForm(answers.toString()); Apply apply = getApply(1L, recruit, applicant, applicationForm, SUBMITTED); - when(applyRepository.findByMemberIdAndRecruitIdInActiveRecruit(eq(applicant.getId()), eq(recruit.getId()), any())) + when(applyRepository.findByApplicantIdAndRecruitIdInActiveRecruit(eq(applicant.getId()), eq(recruit.getId()), any())) .thenReturn(Optional.of(apply)); // when, then @@ -373,15 +373,15 @@ class ApplyServiceTest extends UnitTestSupport { @Test void 프로필_저장_전에_임시저장_시도_시_실패() { // given - long memberId = 1L; + long applicantId = 1L; long recruitId = 1L; Map answers = Map.of("1", "답변1"); - given(applyRepository.findByMemberIdAndRecruitIdInActiveRecruit(eq(memberId), eq(recruitId), any())) + given(applyRepository.findByApplicantIdAndRecruitIdInActiveRecruit(eq(applicantId), eq(recruitId), any())) .willReturn(Optional.empty()); // expected - assertThatThrownBy(() -> applyService.saveApplicationTemporarily(memberId, recruitId, answers, List.of())) + assertThatThrownBy(() -> applyService.saveApplicationTemporarily(applicantId, recruitId, answers, List.of())) .isInstanceOf(ApplyException.class) .extracting("errorCode") .isEqualTo(ApplyErrorCode.NOT_FOUND_APPLY); @@ -404,7 +404,7 @@ class ApplyServiceTest extends UnitTestSupport { "3", "답변 3", "4", "답변 4"); - Member applicant = getApplicant(1L, "email@test.com"); + Applicant applicant = getApplicant(1L, "email@test.com"); ApplicationForm oldApplicationForm = ApplicationForm.builder() .id(1L) @@ -415,7 +415,7 @@ class ApplyServiceTest extends UnitTestSupport { String newContent = "newContent"; - when(applyRepository.findByMemberIdAndRecruitIdInActiveRecruit(eq(applicant.getId()), eq(recruit.getId()), any())) + when(applyRepository.findByApplicantIdAndRecruitIdInActiveRecruit(eq(applicant.getId()), eq(recruit.getId()), any())) .thenReturn(Optional.of(apply)); when(map2JsonSerializer.serializeAsString(answers)).thenReturn(newContent); @@ -446,7 +446,7 @@ class ApplyServiceTest extends UnitTestSupport { Apply apply = getApply(1L, recruit, getApplicant(1L, "email@test.com"), applicationForm, TEMP_SAVED); - when(applyRepository.findByMemberIdAndRecruitIdInActiveRecruit(eq(1L), eq(recruit.getId()), any())) + when(applyRepository.findByApplicantIdAndRecruitIdInActiveRecruit(eq(1L), eq(recruit.getId()), any())) .thenReturn(Optional.of(apply)); // when @@ -456,7 +456,7 @@ class ApplyServiceTest extends UnitTestSupport { assertThat(apply.getApplicationForm()).isNull(); assertThat(apply.getStatus()).isEqualTo(JOINED); - Member applicant = apply.getMember(); + Applicant applicant = apply.getApplicant(); assertThat(applicant.getName()).isNull(); assertThat(applicant.getPhoneNumber()).isNull(); assertThat(applicant.getJobFamily()).isNull(); @@ -482,9 +482,9 @@ class ApplyServiceTest extends UnitTestSupport { .content("content") .build(), SUBMITTED); - when(applyRepository.findByMemberIdAndRecruitIdInActiveRecruit(eq(1L), eq(recruit.getId()), any())) + when(applyRepository.findByApplicantIdAndRecruitIdInActiveRecruit(eq(1L), eq(recruit.getId()), any())) .thenReturn(Optional.of(apply1)); - when(applyRepository.findByMemberIdAndRecruitIdInActiveRecruit(eq(2L), eq(recruit.getId()), any())) + when(applyRepository.findByApplicantIdAndRecruitIdInActiveRecruit(eq(2L), eq(recruit.getId()), any())) .thenReturn(Optional.of(apply2)); // when, then @@ -510,7 +510,7 @@ class ApplyServiceTest extends UnitTestSupport { Apply apply = getApply(1L, recruit, getApplicant(1L, "email@test.com"), tempApplicationForm, TEMP_SAVED); - when(applyRepository.findByMemberIdAndRecruitIdInActiveRecruit(eq(1L), eq(recruit.getId()), any())) + when(applyRepository.findByApplicantIdAndRecruitIdInActiveRecruit(eq(1L), eq(recruit.getId()), any())) .thenReturn(Optional.of(apply)); when(string2MapSerializer.serializeAsMap(tempApplicationForm.getContent())).thenReturn(answers); @@ -534,7 +534,7 @@ class ApplyServiceTest extends UnitTestSupport { Apply apply = getApply(1L, getActiveRecruit(BE, questions), getApplicant(1L, "email@test.com"), null, JOINED); - when(applyRepository.findByMemberIdAndRecruitIdInActiveRecruit(eq(1L), eq(apply.getRecruit().getId()), any())) + when(applyRepository.findByApplicantIdAndRecruitIdInActiveRecruit(eq(1L), eq(apply.getRecruit().getId()), any())) .thenReturn(Optional.of(apply)); // when, then @@ -545,9 +545,9 @@ class ApplyServiceTest extends UnitTestSupport { @Test void 프로필_저장_성공() { // given - long memberId = 1L; + long applicantId = 1L; long recruitId = 1L; - Member member = getApplicant(memberId, "test@example.com"); + Applicant applicant = getApplicant(applicantId, "test@example.com"); ApplyProfileRequest request = new ApplyProfileRequest( "New Name", "010-1234-5678", @@ -559,34 +559,34 @@ class ApplyServiceTest extends UnitTestSupport { ); Recruit recruit = getActiveRecruit(request.jobFamily(), List.of()); - given(memberRepository.findById(memberId)).willReturn(Optional.of(member)); + given(applicantRepository.findById(applicantId)).willReturn(Optional.of(applicant)); given(recruitRepository.findActiveRecruitById(eq(recruitId), any())).willReturn(recruit); - given(applyRepository.existsByMemberIdAndRecruitIdInActiveRecruit(eq(memberId), eq(recruitId), any())) + given(applyRepository.existsByApplicantIdAndRecruitIdInActiveRecruit(eq(applicantId), eq(recruitId), any())) .willReturn(false); // when - applyService.saveProfile(memberId, recruitId, request); + applyService.saveProfile(applicantId, recruitId, request); // then ArgumentCaptor applyCaptor = ArgumentCaptor.forClass(Apply.class); verify(applyRepository).save(applyCaptor.capture()); Apply savedApply = applyCaptor.getValue(); - assertThat(savedApply.getMember()).isEqualTo(member); + assertThat(savedApply.getApplicant()).isEqualTo(applicant); assertThat(savedApply.getRecruit()).isEqualTo(recruit); assertThat(savedApply.getStatus()).isEqualTo(JOINED); - assertThat(member.getName()).isEqualTo(request.name()); - assertThat(member.getPhoneNumber()).isEqualTo(request.phoneNumber()); - assertThat(member.getJobFamily()).isEqualTo(recruit.getJobFamily()); + assertThat(applicant.getName()).isEqualTo(request.name()); + assertThat(applicant.getPhoneNumber()).isEqualTo(request.phoneNumber()); + assertThat(applicant.getJobFamily()).isEqualTo(recruit.getJobFamily()); } @Test void 프로필_저장_시_Apply가_존재하면_프로필만_업데이트() { // given - long memberId = 1L; + long applicantId = 1L; long recruitId = 1L; - Member member = getApplicant(memberId, "test@example.com"); + Applicant applicant = getApplicant(applicantId, "test@example.com"); ApplyProfileRequest request = new ApplyProfileRequest( "New Name", "010-1234-5678", @@ -598,30 +598,30 @@ class ApplyServiceTest extends UnitTestSupport { ); Recruit recruit = getActiveRecruit(request.jobFamily(), List.of()); - given(memberRepository.findById(memberId)).willReturn(Optional.of(member)); + given(applicantRepository.findById(applicantId)).willReturn(Optional.of(applicant)); given(recruitRepository.findActiveRecruitById(eq(recruitId), any())).willReturn(recruit); - given(applyRepository.existsByMemberIdAndRecruitIdInActiveRecruit(eq(memberId), eq(recruitId), any())) + given(applyRepository.existsByApplicantIdAndRecruitIdInActiveRecruit(eq(applicantId), eq(recruitId), any())) .willReturn(true); // when - applyService.saveProfile(memberId, recruitId, request); + applyService.saveProfile(applicantId, recruitId, request); // then verify(applyRepository, never()).save(any(Apply.class)); - assertThat(member.getName()).isEqualTo(request.name()); - assertThat(member.getPhoneNumber()).isEqualTo(request.phoneNumber()); - assertThat(member.getJobFamily()).isEqualTo(recruit.getJobFamily()); - assertThat(member.getCareerDetails()).isEqualTo(request.careerDetails()); - assertThat(member.getExperiencePeriod()).isEqualTo(request.experiencePeriod()); + assertThat(applicant.getName()).isEqualTo(request.name()); + assertThat(applicant.getPhoneNumber()).isEqualTo(request.phoneNumber()); + assertThat(applicant.getJobFamily()).isEqualTo(recruit.getJobFamily()); + assertThat(applicant.getCareerDetails()).isEqualTo(request.careerDetails()); + assertThat(applicant.getExperiencePeriod()).isEqualTo(request.experiencePeriod()); } @Test void 프로필_저장_시_모집_공고_직군으로_프로필을_저장한다() { // given - long memberId = 1L; + long applicantId = 1L; long recruitId = 1L; - Member member = getApplicant(memberId, "test@example.com"); + Applicant applicant = getApplicant(applicantId, "test@example.com"); ApplyProfileRequest request = new ApplyProfileRequest( "New Name", "010-1234-5678", @@ -633,24 +633,24 @@ class ApplyServiceTest extends UnitTestSupport { ); Recruit recruit = getActiveRecruit(BE, List.of()); - given(memberRepository.findById(memberId)).willReturn(Optional.of(member)); + given(applicantRepository.findById(applicantId)).willReturn(Optional.of(applicant)); given(recruitRepository.findActiveRecruitById(eq(recruitId), any())).willReturn(recruit); - given(applyRepository.existsByMemberIdAndRecruitIdInActiveRecruit(eq(memberId), eq(recruitId), any())) + given(applyRepository.existsByApplicantIdAndRecruitIdInActiveRecruit(eq(applicantId), eq(recruitId), any())) .willReturn(false); // when - applyService.saveProfile(memberId, recruitId, request); + applyService.saveProfile(applicantId, recruitId, request); // then - assertThat(member.getJobFamily()).isEqualTo(BE); + assertThat(applicant.getJobFamily()).isEqualTo(BE); } @Test void 프로필_저장_시_유효하지_않은_모집_공고_식별자면_실패한다() { // given - long memberId = 1L; + long applicantId = 1L; long invalidRecruitId = 999L; - Member member = getApplicant(memberId, "test@example.com"); + Applicant applicant = getApplicant(applicantId, "test@example.com"); ApplyProfileRequest request = new ApplyProfileRequest( "New Name", "010-1234-5678", @@ -661,11 +661,11 @@ class ApplyServiceTest extends UnitTestSupport { List.of(InterestedDomain.GAME.getDescription(), InterestedDomain.EDUCATION.getDescription()) ); - given(memberRepository.findById(memberId)).willReturn(Optional.of(member)); + given(applicantRepository.findById(applicantId)).willReturn(Optional.of(applicant)); given(recruitRepository.findActiveRecruitById(eq(invalidRecruitId), any())).willReturn(null); // when, then - assertThatThrownBy(() -> applyService.saveProfile(memberId, invalidRecruitId, request)) + assertThatThrownBy(() -> applyService.saveProfile(applicantId, invalidRecruitId, request)) .isInstanceOf(RecruitException.class) .extracting("errorCode") .isEqualTo(RecruitErrorCode.NOT_FOUND_RECRUIT); @@ -695,8 +695,8 @@ private Question getQuestion(Long id, int sequence, String title, String label) .build(); } - private Member getApplicant(Long id, String email) { - return Member.builder() + private Applicant getApplicant(Long id, String email) { + return Applicant.builder() .id(id) .email(email) .pin("111111") @@ -705,11 +705,11 @@ private Member getApplicant(Long id, String email) { .build(); } - private Apply getApply(Long id, Recruit recruit, Member applicant, ApplicationForm applicationForm, ApplyStatus status) { + private Apply getApply(Long id, Recruit recruit, Applicant applicant, ApplicationForm applicationForm, ApplyStatus status) { return Apply.builder() .id(id) .recruit(recruit) - .member(applicant) + .applicant(applicant) .applicationForm(applicationForm) .status(status) .build(); diff --git a/src/test/java/org/ject/support/domain/member/repository/MemberAccountQueryRepositoryTest.java b/src/test/java/org/ject/support/domain/member/repository/MemberAccountQueryRepositoryTest.java deleted file mode 100644 index c30379e1..00000000 --- a/src/test/java/org/ject/support/domain/member/repository/MemberAccountQueryRepositoryTest.java +++ /dev/null @@ -1,115 +0,0 @@ -package org.ject.support.domain.member.repository; - -import static org.assertj.core.api.Assertions.assertThat; - -import java.time.LocalDateTime; -import java.util.List; -import org.ject.support.admin.account.dto.AdminAccountSearchCondition; -import org.ject.support.domain.member.MemberStatus; -import org.ject.support.domain.member.MemberType; -import org.ject.support.domain.member.Role; -import org.ject.support.domain.member.dto.MemberAccountProjection; -import org.ject.support.domain.member.entity.Member; -import org.ject.support.testconfig.QueryDslTestConfig; -import org.junit.jupiter.api.Test; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest; -import org.springframework.context.annotation.Import; -import org.springframework.data.domain.Page; -import org.springframework.data.domain.PageRequest; -import org.springframework.test.util.ReflectionTestUtils; - -@Import(QueryDslTestConfig.class) -@DataJpaTest -class MemberAccountQueryRepositoryTest { - - @Autowired - MemberRepository memberRepository; - - @Test - void 필터가_없으면_삭제되지_않은_백오피스_계정만_조회한다() { - // given - Member admin = createMember("admin@ject.kr", "관리자", Role.ADMIN, MemberStatus.ACTIVE); - Member operations = createMember("operations@ject.kr", "운영자", Role.OPERATIONS, MemberStatus.ACTIVE); - Member semester = createMember("semester@ject.kr", "일반회원", Role.SEMESTER, MemberStatus.ACTIVE); - Member deleted = createMember("deleted@ject.kr", "삭제회원", Role.SUPPORTER, MemberStatus.ACTIVE); - ReflectionTestUtils.setField(deleted, "isDeleted", true); - memberRepository.saveAll(List.of(admin, operations, semester, deleted)); - - // when - Page result = memberRepository.findAccounts( - new AdminAccountSearchCondition(null, null), - PageRequest.of(0, 20)); - - // then - assertThat(result.getTotalElements()).isEqualTo(2); - assertThat(result.getContent()) - .extracting(MemberAccountProjection::email) - .containsExactlyInAnyOrder("admin@ject.kr", "operations@ject.kr"); - } - - @Test - void 계정_유형과_상태를_복수_필터로_조회한다() { - // given - memberRepository.saveAll(List.of( - createMember("active-admin@ject.kr", "활성관리자", Role.ADMIN, MemberStatus.ACTIVE), - createMember("locked-admin@ject.kr", "잠긴관리자", Role.ADMIN, MemberStatus.LOCKED), - createMember("active-supporter@ject.kr", "활성서포터", Role.SUPPORTER, MemberStatus.ACTIVE), - createMember("locked-operations@ject.kr", "잠긴운영자", Role.OPERATIONS, MemberStatus.LOCKED) - )); - - AdminAccountSearchCondition condition = new AdminAccountSearchCondition( - List.of(Role.ADMIN, Role.SUPPORTER), - List.of(MemberStatus.ACTIVE)); - - // when - Page result = memberRepository.findAccounts(condition, PageRequest.of(0, 20)); - - // then - assertThat(result.getTotalElements()).isEqualTo(2); - assertThat(result.getContent()) - .extracting(MemberAccountProjection::email) - .containsExactlyInAnyOrder("active-admin@ject.kr", "active-supporter@ject.kr"); - } - - @Test - void 페이지네이션과_기본_정렬을_적용한다() { - // given - Member first = createMember("first@ject.kr", "첫번째", Role.ADMIN, MemberStatus.ACTIVE); - Member second = createMember("second@ject.kr", "두번째", Role.ADMIN, MemberStatus.ACTIVE); - Member third = createMember("third@ject.kr", "세번째", Role.ADMIN, MemberStatus.ACTIVE); - setCreatedAt(first, LocalDateTime.of(2026, 1, 1, 0, 0)); - setCreatedAt(second, LocalDateTime.of(2026, 1, 2, 0, 0)); - setCreatedAt(third, LocalDateTime.of(2026, 1, 3, 0, 0)); - memberRepository.saveAll(List.of(first, second, third)); - memberRepository.flush(); - - // when - Page result = memberRepository.findAccounts( - new AdminAccountSearchCondition(null, null), - PageRequest.of(0, 2)); - - // then - assertThat(result.getTotalElements()).isEqualTo(3); - assertThat(result.getContent()) - .extracting(MemberAccountProjection::email) - .containsExactly("third@ject.kr", "second@ject.kr"); - } - - private Member createMember(String email, String name, Role role, MemberStatus status) { - return Member.builder() - .email(email) - .name(name) - .phoneNumber("01012345678") - .memberType(MemberType.SEMESTER) - .semesterId(1L) - .role(role) - .pin("encoded-password") - .status(status) - .build(); - } - - private void setCreatedAt(Member member, LocalDateTime createdAt) { - ReflectionTestUtils.setField(member, "createdAt", createdAt); - } -} diff --git a/src/test/java/org/ject/support/domain/member/repository/MemberQueryRepositoryTest.java b/src/test/java/org/ject/support/domain/member/repository/MemberQueryRepositoryTest.java index 3c6c1e79..2d0d34af 100644 --- a/src/test/java/org/ject/support/domain/member/repository/MemberQueryRepositoryTest.java +++ b/src/test/java/org/ject/support/domain/member/repository/MemberQueryRepositoryTest.java @@ -148,51 +148,6 @@ void setUp() { assertThat(teamMemberNames.frontendDevelopers()).contains("폴백"); } - @Test - void 전달_받은_ID_중_지원서를_제출하지_않은_사용자의_이메일_목록_조회() { - // given - Member be5 = createMember("이젝트", "01011112231", "be5@test.kr", BE); // 5 - Member be6 = createMember("박젝트", "01011112232", "be6@test.kr", BE); // 6 - Member pm7 = createMember("서젝트", "01011112233", "pm7@test.kr", PM); // 7 - Member fe8 = createMember("양젝트", "01011112234", "fe8@test.kr", FE); // 8 - Member be9 = createMember("조젝트", "01011112235", "be9@test.kr", BE); // 9 - Member pd10 = createMember("표젝트", "01011112236", "pd10@test.kr", PD); // 10 - List testMembers = memberRepository.saveAll(List.of(be5, be6, pm7, fe8, be9, pd10)); - - Semester savedSemester = semesterRepository.save(Semester.builder() - .name("1기") - .isRecruiting(true) - .build()); - Recruit pmRecruit = createRecruit(savedSemester, PM); - Recruit pdRecruit = createRecruit(savedSemester, PD); - Recruit feRecruit = createRecruit(savedSemester, FE); - Recruit beRecruit = createRecruit(savedSemester, BE); - recruitRepository.saveAll(List.of(pmRecruit, pdRecruit, feRecruit, beRecruit)); - - // 일부 회원만 지원서 제출 - Apply be5Apply = applyRepository.save(createApply(beRecruit, be5, SUBMITTED)); - applyRepository.save(createApply(beRecruit, be6, JOINED)); - Apply pm7Apply = applyRepository.save(createApply(pmRecruit, pm7, SUBMITTED)); - Apply fe8Apply = applyRepository.save(createApply(feRecruit, fe8, SUBMITTED)); - applyRepository.save(createApply(beRecruit, be9, JOINED)); - applyRepository.save(createApply(pdRecruit, pd10, TEMP_SAVED)); - - applicationFormRepository.saveAll(List.of( - createApplicationForm(be5Apply), - createApplicationForm(pm7Apply), - createApplicationForm(fe8Apply))); - - List applicantIds = testMembers.stream().map(Member::getId).toList(); - - // when - List result = memberRepository.findEmailsByIdsAndNotSubmitted(applicantIds); - - // then - assertThat(result).hasSize(3) - .containsExactlyInAnyOrder(be6.getEmail(), be9.getEmail(), pd10.getEmail()); - } - - private Semester createSemester(String name) { return Semester.builder() .name(name) @@ -258,18 +213,4 @@ private Recruit createRecruit(Semester semester, JobFamily jobFamily) { .build(); } - private Apply createApply(Recruit recruit, Member member, ApplyStatus status) { - return applyRepository.save(Apply.builder() - .member(member) - .recruit(recruit) - .status(status) - .build()); - } - - private ApplicationForm createApplicationForm(Apply apply) { - return ApplicationForm.builder() - .content("content") - .apply(apply) - .build(); - } } diff --git a/src/test/java/org/ject/support/domain/recruit/repository/ApplicationFormRepositoryTest.java b/src/test/java/org/ject/support/domain/recruit/repository/ApplicationFormRepositoryTest.java index f08d4c10..da3c6860 100644 --- a/src/test/java/org/ject/support/domain/recruit/repository/ApplicationFormRepositoryTest.java +++ b/src/test/java/org/ject/support/domain/recruit/repository/ApplicationFormRepositoryTest.java @@ -9,8 +9,8 @@ import org.ject.support.domain.member.JobFamily; import org.ject.support.domain.member.MemberStatus; import org.ject.support.domain.member.Role; -import org.ject.support.domain.member.entity.Member; -import org.ject.support.domain.member.repository.MemberRepository; +import org.ject.support.domain.applicant.entity.Applicant; +import org.ject.support.domain.applicant.repository.ApplicantRepository; import org.ject.support.domain.recruit.domain.Recruit; import org.ject.support.domain.recruit.domain.Semester; import org.ject.support.testconfig.QueryDslTestConfig; @@ -31,7 +31,7 @@ class ApplicationFormRepositoryTest { RecruitRepository recruitRepository; @Autowired - MemberRepository memberRepository; + ApplicantRepository applicantRepository; @Autowired ApplyRepository applyRepository; @@ -58,7 +58,7 @@ void check_apply_submit() { .jobFamily(JobFamily.BE) .build()); - Member member = memberRepository.save(Member.builder() + Applicant applicant = applicantRepository.save(Applicant.builder() .email("test32@gmail.com") .semesterId(savedSemester.getId()) .jobFamily(JobFamily.BE) @@ -70,7 +70,7 @@ void check_apply_submit() { .build()); Apply apply = applyRepository.save(Apply.builder() - .member(member) + .applicant(applicant) .recruit(recruit) .status(ApplyStatus.JOINED) .build()); @@ -82,10 +82,10 @@ void check_apply_submit() { .build()); // when - boolean result = applicationFormRepository.existsByMemberId(member.getId(), LocalDateTime.now()); + boolean result = applicationFormRepository.existsByApplicantId(applicant.getId(), LocalDateTime.now()); // then Assertions.assertThat(result).isTrue(); } -} \ No newline at end of file +} From 9b145c1580e9ddf88ba394af5e0efa187fcb1d3d Mon Sep 17 00:00:00 2001 From: kangkigs Date: Fri, 12 Jun 2026 13:17:30 +0900 Subject: [PATCH 4/4] =?UTF-8?q?[chore]=20=EC=9E=84=EC=8B=9C=20dev=20?= =?UTF-8?q?=EB=B8=8C=EB=9E=9C=EC=B9=98=20ci=EB=A3=B0=20=EC=A0=9C=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/continuous-intergration.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.github/workflows/continuous-intergration.yml b/.github/workflows/continuous-intergration.yml index c2d3f69d..f11aac4f 100644 --- a/.github/workflows/continuous-intergration.yml +++ b/.github/workflows/continuous-intergration.yml @@ -5,7 +5,6 @@ on: branches: - "main" - "dev" - - "tmp/dev" #Todo: 도메인 분리를 위한 임시 dev, dev로 병합시 제거 permissions: checks: write