diff --git a/src/main/java/com/assu/server/domain/auth/controller/AuthController.java b/src/main/java/com/assu/server/domain/auth/controller/AuthController.java index b0e78e79..7351921e 100644 --- a/src/main/java/com/assu/server/domain/auth/controller/AuthController.java +++ b/src/main/java/com/assu/server/domain/auth/controller/AuthController.java @@ -13,6 +13,7 @@ import com.assu.server.domain.auth.dto.ssu.USaintAuthResponse; import com.assu.server.domain.auth.service.*; import com.assu.server.domain.user.entity.enums.University; +import com.assu.server.domain.auth.dto.verification.VerificationRequestDTO; import com.assu.server.global.apiPayload.BaseResponse; import com.assu.server.global.apiPayload.code.status.SuccessStatus; import io.swagger.v3.oas.annotations.Operation; @@ -41,6 +42,7 @@ public class AuthController { private final LogoutService logoutService; private final SSUAuthService ssuAuthService; private final WithdrawalService withdrawalService; + private final VerificationService verificationService; @Operation( summary = "휴대폰 인증번호 발송 API", @@ -82,6 +84,36 @@ public BaseResponse checkAuthNumber( return BaseResponse.onSuccess(SuccessStatus.VERIFY_AUTH_NUMBER_SUCCESS, null); } + @Operation(summary = "전화번호 중복 체크 API", description = "# [v1.0 (2025-01-15)]\n" + + "- 입력한 전화번호가 이미 가입된 사용자가 있는지 확인합니다.\n" + + "- 중복된 전화번호가 있으면 에러를 반환합니다.\n" + + "\n**Request Body:**\n" + + " - `phoneNumber` (String, required): 확인할 전화번호 (010XXXXXXXX 형식)\n" + + "\n**Response:**\n" + + " - 성공 시 200(OK)과 사용 가능 메시지 반환\n" + + " - 중복 시 404(NOT_FOUND)와 에러 메시지 반환") + @PostMapping("/phone-verification/check") + public BaseResponse checkPhoneNumberAvailability( + @RequestBody @Valid VerificationRequestDTO.PhoneVerificationCheckRequest request) { + verificationService.checkPhoneNumberAvailability(request); + return BaseResponse.onSuccess(SuccessStatus._OK, null); + } + + @Operation(summary = "이메일 중복 체크 API", description = "# [v1.0 (2025-01-15)]\n" + + "- 입력한 이메일이 이미 가입된 사용자가 있는지 확인합니다.\n" + + "- 중복된 이메일이 있으면 에러를 반환합니다.\n" + + "\n**Request Body:**\n" + + " - `email` (String, required): 확인할 이메일 주소\n" + + "\n**Response:**\n" + + " - 성공 시 200(OK)과 사용 가능 메시지 반환\n" + + " - 중복 시 404(NOT_FOUND)와 에러 메시지 반환") + @PostMapping("/email-verification/check") + public BaseResponse checkEmailAvailability( + @RequestBody @Valid VerificationRequestDTO.EmailVerificationCheckRequest request) { + verificationService.checkEmailAvailability(request); + return BaseResponse.onSuccess(SuccessStatus._OK, null); + } + @Operation( summary = "학생 회원가입 API", description = "# [v1.2 (2025-09-13)](https://clumsy-seeder-416.notion.site/2241197c19ed81129c85cf5bbe1f7971)\n" + diff --git a/src/main/java/com/assu/server/domain/auth/dto/verification/VerificationRequestDTO.java b/src/main/java/com/assu/server/domain/auth/dto/verification/VerificationRequestDTO.java new file mode 100644 index 00000000..5ef713ed --- /dev/null +++ b/src/main/java/com/assu/server/domain/auth/dto/verification/VerificationRequestDTO.java @@ -0,0 +1,32 @@ +package com.assu.server.domain.auth.dto.verification; + +import jakarta.validation.constraints.Email; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.Pattern; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Getter; +import lombok.NoArgsConstructor; + +public class VerificationRequestDTO { + + @Builder + @Getter + @NoArgsConstructor + @AllArgsConstructor + public static class PhoneVerificationCheckRequest { + @NotBlank(message = "전화번호는 필수입니다.") + @Pattern(regexp = "^010\\d{8}$", message = "올바른 전화번호 형식이 아닙니다.") + private String phoneNumber; + } + + @Builder + @Getter + @NoArgsConstructor + @AllArgsConstructor + public static class EmailVerificationCheckRequest { + @NotBlank(message = "이메일은 필수입니다.") + @Email(message = "올바른 이메일 형식이 아닙니다.") + private String email; + } +} diff --git a/src/main/java/com/assu/server/domain/auth/service/SSUAuthServiceImpl.java b/src/main/java/com/assu/server/domain/auth/service/SSUAuthServiceImpl.java index 5c22bdfc..515d7cfd 100644 --- a/src/main/java/com/assu/server/domain/auth/service/SSUAuthServiceImpl.java +++ b/src/main/java/com/assu/server/domain/auth/service/SSUAuthServiceImpl.java @@ -136,6 +136,63 @@ public USaintAuthResponse uSaintAuth(USaintAuthRequest uSaintAuthRequest) { // 매핑된 Enum 값 저장 switch (majorStr) { + // 인문대학 + case "기독교학과" -> usaintAuthResponse.setMajor(Major.CHRISTIAN_STUDIES); + case "국어국문학과" -> usaintAuthResponse.setMajor(Major.KOREAN_LITERATURE); + case "영어영문학과" -> usaintAuthResponse.setMajor(Major.ENGLISH_LITERATURE); + case "독어독문학과" -> usaintAuthResponse.setMajor(Major.GERMAN_LITERATURE); + case "불어불문학과" -> usaintAuthResponse.setMajor(Major.FRENCH_LITERATURE); + case "중어중문학과" -> usaintAuthResponse.setMajor(Major.CHINESE_LITERATURE); + case "일어일문학과" -> usaintAuthResponse.setMajor(Major.JAPANESE_LITERATURE); + case "철학과" -> usaintAuthResponse.setMajor(Major.PHILOSOPHY); + case "사학과" -> usaintAuthResponse.setMajor(Major.HISTORY); + case "예술창작학부" -> usaintAuthResponse.setMajor(Major.CREATIVE_ARTS); + case "스포츠학부" -> usaintAuthResponse.setMajor(Major.SPORTS); + + // 자연과학대학 + case "수학과" -> usaintAuthResponse.setMajor(Major.MATHEMATICS); + case "화학과" -> usaintAuthResponse.setMajor(Major.CHEMISTRY); + case "의생명시스템학부" -> usaintAuthResponse.setMajor(Major.BIOMEDICAL_SYSTEMS); + case "물리학과" -> usaintAuthResponse.setMajor(Major.PHYSICS); + case "정보통계ㆍ보험수리학과" -> usaintAuthResponse.setMajor(Major.STATISTICS_ACTUARIAL); + + // 법과대학 + case "법학과" -> usaintAuthResponse.setMajor(Major.LAW); + case "국제법무학과" -> usaintAuthResponse.setMajor(Major.INTERNATIONAL_LAW); + + // 사회과학대학 + case "사회복지학부" -> usaintAuthResponse.setMajor(Major.SOCIAL_WELFARE); + case "정치외교학과" -> usaintAuthResponse.setMajor(Major.POLITICAL_SCIENCE); + case "언론홍보학과" -> usaintAuthResponse.setMajor(Major.MEDIA_COMMUNICATION); + case "행정학부" -> usaintAuthResponse.setMajor(Major.PUBLIC_ADMINISTRATION); + case "정보사회학과" -> usaintAuthResponse.setMajor(Major.INFORMATION_SOCIETY); + case "평생교육학과" -> usaintAuthResponse.setMajor(Major.LIFELONG_EDUCATION); + + // 경제통상대학 + case "경제학과" -> usaintAuthResponse.setMajor(Major.ECONOMICS); + case "금융경제학과" -> usaintAuthResponse.setMajor(Major.FINANCIAL_ECONOMICS); + case "글로벌통상학과" -> usaintAuthResponse.setMajor(Major.GLOBAL_TRADE); + case "국제무역학과" -> usaintAuthResponse.setMajor(Major.INTERNATIONAL_TRADE); + + // 경영대학 + case "경영학부" -> usaintAuthResponse.setMajor(Major.BUSINESS_ADMINISTRATION); + case "회계학과" -> usaintAuthResponse.setMajor(Major.ACCOUNTING); + case "벤처경영학과" -> usaintAuthResponse.setMajor(Major.VENTURE_MANAGEMENT); + case "복지경영학과" -> usaintAuthResponse.setMajor(Major.WELFARE_MANAGEMENT); + case "벤처중소기업학과" -> usaintAuthResponse.setMajor(Major.VENTURE_SME); + case "금융학부" -> usaintAuthResponse.setMajor(Major.FINANCE); + case "혁신경영학과" -> usaintAuthResponse.setMajor(Major.INNOVATION_MANAGEMENT); + case "회계세무학과" -> usaintAuthResponse.setMajor(Major.ACCOUNTING_TAX); + + // 공과대학 + case "화학공학과" -> usaintAuthResponse.setMajor(Major.CHEMICAL_ENGINEERING); + case "전기공학부" -> usaintAuthResponse.setMajor(Major.ELECTRICAL_ENGINEERING); + case "건축학부" -> usaintAuthResponse.setMajor(Major.ARCHITECTURE); + case "산업ㆍ정보시스템공학과" -> usaintAuthResponse.setMajor(Major.INDUSTRIAL_INFO_SYSTEMS); + case "기계공학부" -> usaintAuthResponse.setMajor(Major.MECHANICAL_ENGINEERING); + case "신소재공학과" -> usaintAuthResponse.setMajor(Major.MATERIALS_SCIENCE); + + // IT대학 case "컴퓨터학부" -> usaintAuthResponse.setMajor(Major.COM); case "소프트웨어학부" -> usaintAuthResponse.setMajor(Major.SW); case "글로벌미디어학부" -> usaintAuthResponse.setMajor(Major.GM); @@ -143,13 +200,17 @@ public USaintAuthResponse uSaintAuth(USaintAuthRequest uSaintAuthRequest) { case "AI융합학부" -> usaintAuthResponse.setMajor(Major.AI); case "전자정보공학부" -> usaintAuthResponse.setMajor(Major.EE); case "정보보호학과" -> usaintAuthResponse.setMajor(Major.IP); + + // 자유전공학부 + case "자유전공학부" -> usaintAuthResponse.setMajor(Major.LIBERAL_ARTS); + default -> { log.debug("{} is not a supported major.", majorStr); throw new CustomAuthException(ErrorStatus.SSU_SAINT_UNSUPPORTED_MAJOR); } } } - case "과정/학적" -> usaintAuthResponse.setEnrollmentStatus(strong.text()); + case "과정/학기" -> usaintAuthResponse.setEnrollmentStatus(strong.text()); case "학년/학기" -> usaintAuthResponse.setYearSemester(strong.text()); } } @@ -164,8 +225,8 @@ private ResponseEntity requestUSaintSSO(String sToken, String sIdno) { .uri(url) .header("Cookie", "sToken=" + sToken + "; sIdno=" + sIdno) .retrieve() - .toEntity(String.class) // ResponseEntity 전체 반환 (body + header 포함) - .block(); // 동기 방식 + .toEntity(String.class) // ResponseEntity 전체 반환 (body + header 포함) + .block(); // 동기 방식 } private ResponseEntity requestUSaintPortal(StringBuilder cookie) { diff --git a/src/main/java/com/assu/server/domain/auth/service/VerificationService.java b/src/main/java/com/assu/server/domain/auth/service/VerificationService.java new file mode 100644 index 00000000..aaf28237 --- /dev/null +++ b/src/main/java/com/assu/server/domain/auth/service/VerificationService.java @@ -0,0 +1,11 @@ +package com.assu.server.domain.auth.service; + +import com.assu.server.domain.auth.dto.verification.VerificationRequestDTO; + +public interface VerificationService { + void checkPhoneNumberAvailability( + VerificationRequestDTO.PhoneVerificationCheckRequest request); + + void checkEmailAvailability( + VerificationRequestDTO.EmailVerificationCheckRequest request); +} diff --git a/src/main/java/com/assu/server/domain/auth/service/VerificationServiceImpl.java b/src/main/java/com/assu/server/domain/auth/service/VerificationServiceImpl.java new file mode 100644 index 00000000..85cc43ef --- /dev/null +++ b/src/main/java/com/assu/server/domain/auth/service/VerificationServiceImpl.java @@ -0,0 +1,39 @@ +package com.assu.server.domain.auth.service; + +import com.assu.server.domain.auth.dto.verification.VerificationRequestDTO; +import com.assu.server.domain.auth.exception.CustomAuthException; +import com.assu.server.domain.auth.repository.CommonAuthRepository; +import com.assu.server.domain.member.repository.MemberRepository; +import com.assu.server.global.apiPayload.code.status.ErrorStatus; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; + +@Service +@RequiredArgsConstructor +public class VerificationServiceImpl implements VerificationService { + + private final MemberRepository memberRepository; + private final CommonAuthRepository commonAuthRepository; + + @Override + public void checkPhoneNumberAvailability( + VerificationRequestDTO.PhoneVerificationCheckRequest request) { + + boolean exists = memberRepository.existsByPhoneNum(request.getPhoneNumber()); + + if (exists) { + throw new CustomAuthException(ErrorStatus.EXISTED_PHONE); + } + } + + @Override + public void checkEmailAvailability( + VerificationRequestDTO.EmailVerificationCheckRequest request) { + + boolean exists = commonAuthRepository.existsByEmail(request.getEmail()); + + if (exists) { + throw new CustomAuthException(ErrorStatus.EXISTED_EMAIL); + } + } +} diff --git a/src/main/java/com/assu/server/domain/user/entity/enums/Department.java b/src/main/java/com/assu/server/domain/user/entity/enums/Department.java index 668c9dd3..37b91f97 100644 --- a/src/main/java/com/assu/server/domain/user/entity/enums/Department.java +++ b/src/main/java/com/assu/server/domain/user/entity/enums/Department.java @@ -1,7 +1,15 @@ package com.assu.server.domain.user.entity.enums; public enum Department { - IT("IT대학"); + HUMANITIES("인문대학"), + NATURAL_SCIENCE("자연과학대학"), + LAW("법과대학"), + SOCIAL_SCIENCE("사회과학대학"), + ECONOMICS("경제통상대학"), + BUSINESS("경영대학"), + ENGINEERING("공과대학"), + IT("IT대학"), + LIBERAL_ARTS("자유전공학부"); private final String displayName; diff --git a/src/main/java/com/assu/server/domain/user/entity/enums/Major.java b/src/main/java/com/assu/server/domain/user/entity/enums/Major.java index 2fbf0764..302babe1 100644 --- a/src/main/java/com/assu/server/domain/user/entity/enums/Major.java +++ b/src/main/java/com/assu/server/domain/user/entity/enums/Major.java @@ -1,13 +1,73 @@ package com.assu.server.domain.user.entity.enums; public enum Major { + // 인문대학 + CHRISTIAN_STUDIES(Department.HUMANITIES, "기독교학과"), + KOREAN_LITERATURE(Department.HUMANITIES, "국어국문학과"), + ENGLISH_LITERATURE(Department.HUMANITIES, "영어영문학과"), + GERMAN_LITERATURE(Department.HUMANITIES, "독어독문학과"), + FRENCH_LITERATURE(Department.HUMANITIES, "불어불문학과"), + CHINESE_LITERATURE(Department.HUMANITIES, "중어중문학과"), + JAPANESE_LITERATURE(Department.HUMANITIES, "일어일문학과"), + PHILOSOPHY(Department.HUMANITIES, "철학과"), + HISTORY(Department.HUMANITIES, "사학과"), + CREATIVE_ARTS(Department.HUMANITIES, "예술창작학부"), + SPORTS(Department.HUMANITIES, "스포츠학부"), + + // 자연과학대학 + MATHEMATICS(Department.NATURAL_SCIENCE, "수학과"), + CHEMISTRY(Department.NATURAL_SCIENCE, "화학과"), + BIOMEDICAL_SYSTEMS(Department.NATURAL_SCIENCE, "의생명시스템학부"), + PHYSICS(Department.NATURAL_SCIENCE, "물리학과"), + STATISTICS_ACTUARIAL(Department.NATURAL_SCIENCE, "정보통계ㆍ보험수리학과"), + + // 법과대학 + LAW(Department.LAW, "법학과"), + INTERNATIONAL_LAW(Department.LAW, "국제법무학과"), + + // 사회과학대학 + SOCIAL_WELFARE(Department.SOCIAL_SCIENCE, "사회복지학부"), + POLITICAL_SCIENCE(Department.SOCIAL_SCIENCE, "정치외교학과"), + MEDIA_COMMUNICATION(Department.SOCIAL_SCIENCE, "언론홍보학과"), + PUBLIC_ADMINISTRATION(Department.SOCIAL_SCIENCE, "행정학부"), + INFORMATION_SOCIETY(Department.SOCIAL_SCIENCE, "정보사회학과"), + LIFELONG_EDUCATION(Department.SOCIAL_SCIENCE, "평생교육학과"), + + // 경제통상대학 + ECONOMICS(Department.ECONOMICS, "경제학과"), + FINANCIAL_ECONOMICS(Department.ECONOMICS, "금융경제학과"), + GLOBAL_TRADE(Department.ECONOMICS, "글로벌통상학과"), + INTERNATIONAL_TRADE(Department.ECONOMICS, "국제무역학과"), + + // 경영대학 + BUSINESS_ADMINISTRATION(Department.BUSINESS, "경영학부"), + ACCOUNTING(Department.BUSINESS, "회계학과"), + VENTURE_MANAGEMENT(Department.BUSINESS, "벤처경영학과"), + WELFARE_MANAGEMENT(Department.BUSINESS, "복지경영학과"), + VENTURE_SME(Department.BUSINESS, "벤처중소기업학과"), + FINANCE(Department.BUSINESS, "금융학부"), + INNOVATION_MANAGEMENT(Department.BUSINESS, "혁신경영학과"), + ACCOUNTING_TAX(Department.BUSINESS, "회계세무학과"), + + // 공과대학 + CHEMICAL_ENGINEERING(Department.ENGINEERING, "화학공학과"), + ELECTRICAL_ENGINEERING(Department.ENGINEERING, "전기공학부"), + ARCHITECTURE(Department.ENGINEERING, "건축학부"), + INDUSTRIAL_INFO_SYSTEMS(Department.ENGINEERING, "산업ㆍ정보시스템공학과"), + MECHANICAL_ENGINEERING(Department.ENGINEERING, "기계공학부"), + MATERIALS_SCIENCE(Department.ENGINEERING, "신소재공학과"), + + // IT대학 SW(Department.IT, "소프트웨어학부"), - GM(Department.IT, "글로벌미디어학과"), + GM(Department.IT, "글로벌미디어학부"), COM(Department.IT, "컴퓨터학부"), EE(Department.IT, "전자정보공학부"), IP(Department.IT, "정보보호학과"), - AI(Department.IT, "AI융합학과"), - MB(Department.IT, "미디어경영학과"); + AI(Department.IT, "AI융합학부"), + MB(Department.IT, "미디어경영학과"), + + // 자유전공학부 + LIBERAL_ARTS(Department.LIBERAL_ARTS, "자유전공학부"); private final Department department; private final String displayName;