diff --git a/src/main/java/com/tools/seoultech/timoproject/auth/jwt/filter/JwtAuthenticationFilter.java b/src/main/java/com/tools/seoultech/timoproject/auth/jwt/filter/JwtAuthenticationFilter.java index 160c5f0c..d54515f7 100644 --- a/src/main/java/com/tools/seoultech/timoproject/auth/jwt/filter/JwtAuthenticationFilter.java +++ b/src/main/java/com/tools/seoultech/timoproject/auth/jwt/filter/JwtAuthenticationFilter.java @@ -61,7 +61,8 @@ public class JwtAuthenticationFilter extends OncePerRequestFilter { new AntPathRequestMatcher("/v3/api-docs/**"), new AntPathRequestMatcher("/swagger-ui/**"), new AntPathRequestMatcher("/swagger-ui.html"), - new AntPathRequestMatcher("/api/v1/univ/**") + new AntPathRequestMatcher("/api/v1/univ/**"), + new AntPathRequestMatcher("/api/v1/admin/universities/*") ); @Override diff --git a/src/main/java/com/tools/seoultech/timoproject/auth/univ/UnivApiController.java b/src/main/java/com/tools/seoultech/timoproject/auth/univ/UnivApiController.java deleted file mode 100644 index 51127f2b..00000000 --- a/src/main/java/com/tools/seoultech/timoproject/auth/univ/UnivApiController.java +++ /dev/null @@ -1,80 +0,0 @@ -package com.tools.seoultech.timoproject.auth.univ; - -import com.tools.seoultech.timoproject.global.APIErrorResponse; -import com.tools.seoultech.timoproject.global.annotation.CurrentMemberId; -import com.tools.seoultech.timoproject.global.constant.ErrorCode; -import com.tools.seoultech.timoproject.riot.dto.APIDataResponse; -import jakarta.validation.Valid; -import lombok.RequiredArgsConstructor; -import org.springframework.http.HttpStatus; -import org.springframework.http.ResponseEntity; -import org.springframework.web.bind.annotation.*; - -import java.io.IOException; - -@RestController -@RequestMapping("/api/v1/auth/univ") -@RequiredArgsConstructor -public class UnivApiController { - private final UnivApiFacade univApiFacade; - - @PostMapping("/checkUniv") - public ResponseEntity checkUniv(@Valid @RequestBody UnivRequestDTO univRequestDto) throws Exception { - univApiFacade.checkUniv(univRequestDto); - return ResponseEntity - .status(HttpStatus.OK) - .body(APIErrorResponse.of(true, ErrorCode.OK)); - } - @PostMapping("/request") - public ResponseEntity certify(@Valid @RequestBody UnivRequestDTO univRequestDto) throws Exception { - Boolean result = univApiFacade.certify(univRequestDto); - APIErrorResponse response = result ? APIErrorResponse.of(result, ErrorCode.OK) : APIErrorResponse.of(result, ErrorCode.FAILED_UNIV_CERTIFY); - return ResponseEntity - .status(HttpStatus.OK) - .body(response); - } - @PostMapping("/verify") - public ResponseEntity verify(@RequestBody UnivRequestDTO univRequestDto, Integer code) throws Exception{ - Boolean result = univApiFacade.verify(univRequestDto, code); - APIErrorResponse response = result ? APIErrorResponse.of(result, ErrorCode.OK) : APIErrorResponse.of(result, ErrorCode.FAILED_UNIV_CERTIFY); - return ResponseEntity - .status(HttpStatus.OK) - .body(APIErrorResponse.of(true, ErrorCode.OK)); - } - - @PostMapping("/getVerifiedUser") - public ResponseEntity getVerifiedUser() throws Exception { - return ResponseEntity - .status(HttpStatus.OK) - .body( - APIDataResponse.of(univApiFacade.getVerifiedUserList()) - ); - } - @PostMapping("/checkStatus") - public ResponseEntity checkStatus(@RequestBody UnivRequestDTO univRequestDto) throws Exception { - return ResponseEntity - .status(HttpStatus.OK) - .body( - APIDataResponse.of(univApiFacade.checkStatus(univRequestDto)) - ); - } - - @DeleteMapping("/delete/me") - public ResponseEntity deleteUniv(@CurrentMemberId Long memberId) throws Exception{ - univApiFacade.deleteCertifiedUniv(memberId); - return ResponseEntity.noContent().build(); - } - - @ExceptionHandler(value = {IOException.class}) - public ResponseEntity handleException(IOException e) { - return ResponseEntity - .status(HttpStatus.OK) - .body( - APIErrorResponse.of( - false, - ErrorCode.INVALID_INPUT_VALUE, - e.getMessage() - ) - ); - } -} diff --git a/src/main/java/com/tools/seoultech/timoproject/auth/univ/UnivApiFacade.java b/src/main/java/com/tools/seoultech/timoproject/auth/univ/UnivApiFacade.java index 1da7cdee..f24cf6b0 100644 --- a/src/main/java/com/tools/seoultech/timoproject/auth/univ/UnivApiFacade.java +++ b/src/main/java/com/tools/seoultech/timoproject/auth/univ/UnivApiFacade.java @@ -1,12 +1,11 @@ package com.tools.seoultech.timoproject.auth.univ; -public interface UnivApiFacade { - void checkUniv(UnivRequestDTO requestDto) throws Exception; - Boolean certify(UnivRequestDTO requestDto) throws Exception; - Boolean verify(UnivRequestDTO requestDto, Integer code) throws Exception; - Object getVerifiedUserList() throws Exception; - Object checkStatus(UnivRequestDTO requestDto) throws Exception; +import java.io.IOException; - void deleteCertifiedUniv(Long memberId) throws Exception; +public interface UnivApiFacade { + void checkUniv(UnivRequestDTO requestDto); + Boolean certify(UnivRequestDTO requestDto) throws IOException; + Boolean verify(UnivRequestDTO requestDto, Integer code, Long memberId); // ⭐️ 수정 + void deleteCertifiedUniv(Long memberId); } diff --git a/src/main/java/com/tools/seoultech/timoproject/auth/univ/UnivApiFacadeImpl.java b/src/main/java/com/tools/seoultech/timoproject/auth/univ/UnivApiFacadeImpl.java index 2df77ee4..f811f5c3 100644 --- a/src/main/java/com/tools/seoultech/timoproject/auth/univ/UnivApiFacadeImpl.java +++ b/src/main/java/com/tools/seoultech/timoproject/auth/univ/UnivApiFacadeImpl.java @@ -1,12 +1,7 @@ package com.tools.seoultech.timoproject.auth.univ; -import com.tools.seoultech.timoproject.member.domain.entity.Member; -import com.tools.seoultech.timoproject.member.domain.entity.embeddableType.CertifiedUnivInfo; -import com.tools.seoultech.timoproject.member.service.MemberService; -import jakarta.persistence.EntityManager; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Component; - import java.io.IOException; @Component @@ -16,34 +11,22 @@ public class UnivApiFacadeImpl implements UnivApiFacade { @Override - public void checkUniv(UnivRequestDTO requestDto) throws Exception { + public void checkUniv(UnivRequestDTO requestDto) { univService.checkUniv(requestDto.univName()); } @Override public Boolean certify(UnivRequestDTO requestDto) throws IOException { - univService.checkUniv(requestDto.univName()); - Boolean result = univService.certifyUniv(requestDto); - return result; - } - - @Override - public Boolean verify(UnivRequestDTO requestDto, Integer code) throws IOException { - Boolean result = univService.verifyRequest(requestDto, code); - return result; - } - @Override - public Object getVerifiedUserList() throws Exception { - return univService.getVerifiedUserList(); + return univService.certifyUniv(requestDto); } @Override - public Object checkStatus(UnivRequestDTO requestDto) throws Exception { - return univService.checkStatus(requestDto); + public Boolean verify(UnivRequestDTO requestDto, Integer code, Long memberId) { + return univService.verifyRequest(requestDto, code, memberId); } @Override - public void deleteCertifiedUniv(Long memberId) throws Exception{ + public void deleteCertifiedUniv(Long memberId) { univService.deleteCertifiedMember(memberId); } } diff --git a/src/main/java/com/tools/seoultech/timoproject/auth/univ/UnivService.java b/src/main/java/com/tools/seoultech/timoproject/auth/univ/UnivService.java index bdabc623..ffac670e 100644 --- a/src/main/java/com/tools/seoultech/timoproject/auth/univ/UnivService.java +++ b/src/main/java/com/tools/seoultech/timoproject/auth/univ/UnivService.java @@ -1,6 +1,10 @@ package com.tools.seoultech.timoproject.auth.univ; import com.fasterxml.jackson.databind.ObjectMapper; +import com.tools.seoultech.timoproject.auth.univ.entity.UnivVerification; +import com.tools.seoultech.timoproject.auth.univ.entity.University; +import com.tools.seoultech.timoproject.auth.univ.repository.UnivVerificationRepository; +import com.tools.seoultech.timoproject.auth.univ.repository.UniversityRepository; import com.tools.seoultech.timoproject.global.constant.ErrorCode; import com.tools.seoultech.timoproject.global.exception.BusinessException; import com.tools.seoultech.timoproject.member.MemberRepository; @@ -11,10 +15,16 @@ import jakarta.persistence.EntityManager; import lombok.RequiredArgsConstructor; import org.springframework.beans.factory.annotation.Value; +import org.springframework.mail.SimpleMailMessage; +import org.springframework.mail.javamail.JavaMailSender; +import org.springframework.scheduling.annotation.Async; import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; import java.io.IOException; +import java.time.LocalDateTime; import java.util.Map; +import java.util.concurrent.ThreadLocalRandom; @Service @RequiredArgsConstructor @@ -22,62 +32,81 @@ public class UnivService { @Value("${univ_api_key}") private String api_key; + private final UniversityRepository universityRepository; + private final UnivVerificationRepository univVerificationRepository; + private final JavaMailSender mailSender; private final MemberService memberService; - private final MemberRepository memberRepository; - private final EntityManager entityManager; - public void checkUniv(String univName) throws IOException{ - Map response = UnivCert.check(univName); - if(response.get("success").toString().equals("false")){ - throw new IOException(response.get("message").toString()); - } + @Transactional(readOnly = true) + public void checkUniv(String univName) { + universityRepository.findByName(univName) + .orElseThrow(() -> new BusinessException(ErrorCode.NOT_FOUND_UNIV)); } - public Boolean certifyUniv(UnivRequestDTO requestDto) throws IOException { - Map response = UnivCert.certify(api_key, requestDto.univEmail(), requestDto.univName(), true); - if (response.get("success").toString().equals("false")) { - String message = response.get("message").toString(); - - // 메시지에 따른 구체적인 예외 처리 - if (message.contains("이미 완료된 요청")) { - throw new BusinessException(ErrorCode.ALREADY_USED_UNIV_ACCOUNT); // 902 - } else if (message.contains("일치하지 않는 메일 도메인")) { - throw new BusinessException(ErrorCode.MISMATCHED_EMAIL_DOMAIN); // 1001 - } else { - throw new BusinessException(ErrorCode.FAILED_UNIV_CERTIFY); - } + @Transactional + public Boolean certifyUniv(UnivRequestDTO requestDto) { + University university = universityRepository.findByName(requestDto.univName()) + .orElseThrow(() -> new BusinessException(ErrorCode.NOT_FOUND_UNIV)); + + if (!requestDto.univEmail().endsWith("@" + university.getDomain())) { + throw new BusinessException(ErrorCode.MISMATCHED_EMAIL_DOMAIN); } - return true; - } - public Boolean verifyRequest(UnivRequestDTO requestDto, int code) throws IOException { - Map response = UnivCert.certifyCode(api_key, requestDto.univEmail(), requestDto.univName(), code); - if (response.get("success").toString().equals("false")) { - throw new BusinessException(ErrorCode.FAILED_UNIV_CERTIFY); + + if (memberService.isUnivEmailCertified(requestDto.univEmail())) { + throw new BusinessException(ErrorCode.ALREADY_USED_UNIV_ACCOUNT); } + + String code = createVerificationCode(); + univVerificationRepository.findById(requestDto.univEmail()) + .ifPresentOrElse( + verification -> verification.updateCode(code), + () -> univVerificationRepository.save(new UnivVerification(requestDto.univEmail(), requestDto.univName(), code)) + ); + + sendVerificationEmail(requestDto.univEmail(), code); return true; } - public Object checkStatus(UnivRequestDTO requestDto) throws IOException { - Map response = UnivCert.status(api_key, requestDto.univEmail()); - if(response.get("success").toString().equals("false")){ - throw new IOException(response.get("message").toString()); + + @Transactional + public Boolean verifyRequest(UnivRequestDTO requestDto, int code, Long memberId) { + UnivVerification verification = univVerificationRepository.findById(requestDto.univEmail()) + .orElseThrow(() -> new BusinessException(ErrorCode.NOT_FOUND_CERTIFY_REQUEST)); + + if (LocalDateTime.now().isAfter(verification.getExpiresAt())) { + throw new BusinessException(ErrorCode.EXPIRED_VERIFICATION_CODE); } - return response; - } - public Object getVerifiedUserList() throws IOException { - Map response = UnivCert.list(api_key); - if(response.get("success").toString().equals("false")){ - throw new IOException(response.get("message").toString()); + + if (!verification.getCode().equals(String.valueOf(code))) { + throw new BusinessException(ErrorCode.INVALID_VERIFICATION_CODE); } - return response; + + verification.verify(); + + Member member = memberService.getById(memberId); + + CertifiedUnivInfo univInfo = new CertifiedUnivInfo(verification.getEmail(), verification.getUnivName()); + member.updateCertifiedUnivInfo(univInfo); + + return true; } - public void deleteCertifiedMember(Long memberId) throws IOException { + @Transactional + public void deleteCertifiedMember(Long memberId) { Member member = memberService.getById(memberId); - UnivCert.clear(api_key, member.getCertifiedUnivInfo().getUnivCertifiedEmail()); - member = member.toBuilder() - .certifiedUnivInfo(new CertifiedUnivInfo()) - .build(); - memberRepository.save(member); + member.clearCertifiedUnivInfo(); + } + @Async("notificationTaskExecutor") + public void sendVerificationEmail(String email, String code) { + SimpleMailMessage message = new SimpleMailMessage(); + message.setTo(email); + message.setSubject("[Timo] 대학교 이메일 인증 코드입니다."); + message.setText("인증 코드는 [" + code + "] 입니다. 10분 내에 입력해주세요."); + mailSender.send(message); } + + private String createVerificationCode() { + return String.valueOf(ThreadLocalRandom.current().nextInt(100000, 1000000)); + } + } diff --git a/src/main/java/com/tools/seoultech/timoproject/auth/univ/controller/AdminApiController.java b/src/main/java/com/tools/seoultech/timoproject/auth/univ/controller/AdminApiController.java new file mode 100644 index 00000000..f19d3298 --- /dev/null +++ b/src/main/java/com/tools/seoultech/timoproject/auth/univ/controller/AdminApiController.java @@ -0,0 +1,30 @@ +package com.tools.seoultech.timoproject.auth.univ.controller; + +import com.tools.seoultech.timoproject.auth.univ.service.UniversitySetupService; +import io.swagger.v3.oas.annotations.tags.Tag; +import lombok.RequiredArgsConstructor; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +@RestController +@RequestMapping("/api/v1/admin") +@RequiredArgsConstructor +@Tag(name = "[Admin] UnivApi", description = "Admin Univ API") +public class AdminApiController { + + private final UniversitySetupService universitySetupService; + + @PostMapping("/universities/init") + public ResponseEntity initializeUniversities() { + universitySetupService.initializeUniversities(); + return ResponseEntity.ok("University data initialization triggered."); + } + + @PostMapping("/universities/clear") + public ResponseEntity clearUniversities() { + universitySetupService.clearUniversities(); + return ResponseEntity.ok("University data cleared."); + } +} \ No newline at end of file diff --git a/src/main/java/com/tools/seoultech/timoproject/auth/univ/controller/UnivApiController.java b/src/main/java/com/tools/seoultech/timoproject/auth/univ/controller/UnivApiController.java new file mode 100644 index 00000000..4013ba8f --- /dev/null +++ b/src/main/java/com/tools/seoultech/timoproject/auth/univ/controller/UnivApiController.java @@ -0,0 +1,50 @@ +package com.tools.seoultech.timoproject.auth.univ.controller; + +import com.tools.seoultech.timoproject.auth.univ.UnivApiFacade; +import com.tools.seoultech.timoproject.auth.univ.UnivRequestDTO; +import com.tools.seoultech.timoproject.global.APIErrorResponse; +import com.tools.seoultech.timoproject.global.annotation.CurrentMemberId; +import com.tools.seoultech.timoproject.global.constant.ErrorCode; +import jakarta.validation.Valid; +import lombok.RequiredArgsConstructor; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.*; + +import java.io.IOException; + +@RestController +@RequestMapping("/api/v1/auth/univ") +@RequiredArgsConstructor +public class UnivApiController { + private final UnivApiFacade univApiFacade; + + @PostMapping("/checkUniv") + public ResponseEntity checkUniv(@Valid @RequestBody UnivRequestDTO univRequestDto) { + univApiFacade.checkUniv(univRequestDto); + return ResponseEntity.status(HttpStatus.OK).body(APIErrorResponse.of(true, ErrorCode.OK)); + } + + @PostMapping("/request") + public ResponseEntity certify(@Valid @RequestBody UnivRequestDTO univRequestDto) throws IOException { + Boolean result = univApiFacade.certify(univRequestDto); + APIErrorResponse response = result ? APIErrorResponse.of(true, ErrorCode.OK) : APIErrorResponse.of(false, ErrorCode.FAILED_UNIV_CERTIFY); + return ResponseEntity.status(HttpStatus.OK).body(response); + } + + @PostMapping("/verify/me") + public ResponseEntity verify(@RequestBody UnivRequestDTO univRequestDto, + @RequestParam("code") Integer code, + @CurrentMemberId Long memberId) { + + Boolean result = univApiFacade.verify(univRequestDto, code, memberId); + APIErrorResponse response = result ? APIErrorResponse.of(true, ErrorCode.OK) : APIErrorResponse.of(false, ErrorCode.FAILED_UNIV_CERTIFY); + return ResponseEntity.status(HttpStatus.OK).body(response); + } + + @DeleteMapping("/delete/me") + public ResponseEntity deleteUniv(@CurrentMemberId Long memberId) { + univApiFacade.deleteCertifiedUniv(memberId); + return ResponseEntity.noContent().build(); + } +} \ No newline at end of file diff --git a/src/main/java/com/tools/seoultech/timoproject/auth/univ/entity/UnivVerification.java b/src/main/java/com/tools/seoultech/timoproject/auth/univ/entity/UnivVerification.java new file mode 100644 index 00000000..911bb116 --- /dev/null +++ b/src/main/java/com/tools/seoultech/timoproject/auth/univ/entity/UnivVerification.java @@ -0,0 +1,44 @@ +package com.tools.seoultech.timoproject.auth.univ.entity; + +import jakarta.persistence.*; +import lombok.Getter; +import lombok.NoArgsConstructor; +import java.time.LocalDateTime; + +@Entity +@Getter +@NoArgsConstructor +public class UnivVerification { + + @Id + private String email; + + @Column(nullable = false) + private String univName; + + @Column(nullable = false) + private String code; + + @Column(nullable = false) + private boolean isVerified = false; + + @Column(nullable = false) + private LocalDateTime expiresAt; + + public UnivVerification(String email, String univName, String code) { + this.email = email; + this.univName = univName; + this.code = code; + this.expiresAt = LocalDateTime.now().plusMinutes(10); // 10분 후 만료 + } + + public void updateCode(String newCode) { + this.code = newCode; + this.expiresAt = LocalDateTime.now().plusMinutes(10); + this.isVerified = false; + } + + public void verify() { + this.isVerified = true; + } +} \ No newline at end of file diff --git a/src/main/java/com/tools/seoultech/timoproject/auth/univ/entity/University.java b/src/main/java/com/tools/seoultech/timoproject/auth/univ/entity/University.java new file mode 100644 index 00000000..a5474150 --- /dev/null +++ b/src/main/java/com/tools/seoultech/timoproject/auth/univ/entity/University.java @@ -0,0 +1,25 @@ +package com.tools.seoultech.timoproject.auth.univ.entity; + +import jakarta.persistence.*; +import lombok.Getter; +import lombok.NoArgsConstructor; + +@Entity +@Getter +@NoArgsConstructor +public class University { + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + + @Column(nullable = false, unique = true) + private String name; + + @Column(nullable = false) + private String domain; + + public University(String name, String domain) { + this.name = name; + this.domain = domain; + } +} \ No newline at end of file diff --git a/src/main/java/com/tools/seoultech/timoproject/auth/univ/repository/UnivVerificationRepository.java b/src/main/java/com/tools/seoultech/timoproject/auth/univ/repository/UnivVerificationRepository.java new file mode 100644 index 00000000..e47c7ce8 --- /dev/null +++ b/src/main/java/com/tools/seoultech/timoproject/auth/univ/repository/UnivVerificationRepository.java @@ -0,0 +1,7 @@ +package com.tools.seoultech.timoproject.auth.univ.repository; + +import com.tools.seoultech.timoproject.auth.univ.entity.UnivVerification; +import org.springframework.data.jpa.repository.JpaRepository; + +public interface UnivVerificationRepository extends JpaRepository { +} diff --git a/src/main/java/com/tools/seoultech/timoproject/auth/univ/repository/UniversityRepository.java b/src/main/java/com/tools/seoultech/timoproject/auth/univ/repository/UniversityRepository.java new file mode 100644 index 00000000..0091b042 --- /dev/null +++ b/src/main/java/com/tools/seoultech/timoproject/auth/univ/repository/UniversityRepository.java @@ -0,0 +1,9 @@ +package com.tools.seoultech.timoproject.auth.univ.repository; + +import com.tools.seoultech.timoproject.auth.univ.entity.University; +import org.springframework.data.jpa.repository.JpaRepository; +import java.util.Optional; + +public interface UniversityRepository extends JpaRepository { + Optional findByName(String name); +} diff --git a/src/main/java/com/tools/seoultech/timoproject/auth/univ/service/UniversitySetupService.java b/src/main/java/com/tools/seoultech/timoproject/auth/univ/service/UniversitySetupService.java new file mode 100644 index 00000000..1eca02db --- /dev/null +++ b/src/main/java/com/tools/seoultech/timoproject/auth/univ/service/UniversitySetupService.java @@ -0,0 +1,165 @@ +package com.tools.seoultech.timoproject.auth.univ.service; + +import com.tools.seoultech.timoproject.auth.univ.entity.University; +import com.tools.seoultech.timoproject.auth.univ.repository.UniversityRepository; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.List; + +@Slf4j +@Service +@RequiredArgsConstructor +public class UniversitySetupService { + + private final UniversityRepository universityRepository; + + @Transactional + public void initializeUniversities() { + // 테이블에 데이터가 이미 있으면 중복 저장을 방지하기 위해 실행하지 않음 + if (universityRepository.count() > 0) { + log.info("University table is not empty. Initialization skipped."); + return; + } + + List universities = List.of( + new University("가천대학교", "gachon.ac.kr"), + new University("강원대학교", "kangwon.ac.kr"), + new University("건국대학교", "konkuk.ac.kr"), + new University("건국대학교(글로컬)", "kku.ac.kr"), + new University("경기과학기술대학교", "gtec.ac.kr"), + new University("경기대학교", "kyonggi.ac.kr"), + new University("경북대학교", "knu.ac.kr"), + new University("경인교육대학교", "ginue.ac.kr"), + new University("경희대학교", "khu.ac.kr"), + new University("계원예술대학교", "kaywon.ac.kr"), + new University("고려대학교", "korea.ac.kr"), + new University("광운대학교", "kw.ac.kr"), + new University("국민대학교", "kookmin.ac.kr"), + new University("단국대학교", "dankook.ac.kr"), + new University("덕성여자대학교", "duksung.ac.kr"), + new University("동국대학교", "dongguk.edu"), + new University("동국대학교(경주)", "dongguk.ac.kr"), + new University("동덕여자대학교", "dongduk.ac.kr"), + new University("명지대학교", "mju.ac.kr"), + new University("명지전문대학교", "mjc.ac.kr"), + new University("서강대학교", "sogang.ac.kr"), + new University("서경대학교", "skuniv.ac.kr"), + new University("서울과학기술대학교", "seoultech.ac.kr"), + new University("서울교육대학교", "snue.ac.kr"), + new University("서울대학교", "snu.ac.kr"), + new University("서울시립대학교", "uos.ac.kr"), + new University("서울여자대학교", "swu.ac.kr"), + new University("성균관대학교", "skku.edu"), + new University("성신여자대학교", "sungshin.ac.kr"), + new University("세종대학교", "sju.ac.kr"), + new University("숙명여자대학교", "sookmyung.ac.kr"), + new University("숭실대학교", "soongsil.ac.kr"), + new University("아주대학교", "ajou.ac.kr"), + new University("연세대학교", "yonsei.ac.kr"), + new University("영남대학교", "ynu.ac.kr"), + new University("이화여자대학교", "ewhain.net"), + new University("인천대학교", "inu.ac.kr"), + new University("인하공업전문대학", "itc.ac.kr"), + new University("인하대학교", "inha.ac.kr"), + new University("전남대학교", "jnu.ac.kr"), + new University("전북대학교", "jbnu.ac.kr"), + new University("중앙대학교", "cau.ac.kr"), + new University("충북대학교", "chungbuk.ac.kr"), + new University("한국방송통신대학교", "knou.ac.kr"), + new University("한국공학대학교", "tukorea.ac.kr"), + new University("한국예술종합학교", "karts.ac.kr"), + new University("한국외국어대학교", "hufs.ac.kr"), + new University("한국체육대학교", "knsu.ac.kr"), + new University("한양대학교", "hanyang.ac.kr"), + new University("한양대학교(ERICA)", "hanyang.ac.kr"), + new University("홍익대학교", "hongik.ac.kr"), + new University("대구경북과학기술원", "dgist.ac.kr"), + new University("광주과학기술원", "gist.ac.kr"), + new University("한국과학기술원", "kaist.ac.kr"), + new University("포항공과대학교", "postech.ac.kr"), + new University("울산과학기술원", "unist.ac.kr"), + new University("계명대학교", "kmu.ac.kr"), + new University("조선대학교", "chosun.kr"), + new University("경상국립대학교", "gnu.ac.kr"), + new University("동아대학교", "donga.ac.kr"), + new University("대구대학교", "daegu.ac.kr"), + new University("동의대학교", "deu.ac.kr"), + new University("충남대학교", "cnu.ac.kr"), + new University("부경대학교", "pknu.ac.kr"), + new University("서울사이버대학교", "iscu.ac.kr"), + new University("한양사이버대학교", "hycu.ac.kr"), + new University("원광대학교", "wku.ac.kr"), + new University("경희사이버대학교", "khcu.ac.kr"), + new University("서울디지털대학교", "sdu.ac.kr"), + new University("백석대학교", "bu.ac.kr"), + new University("부천대학교", "bc.ac.kr"), + new University("대구가톨릭대학교", "cu.ac.kr"), + new University("한양여자대학교", "hywoman.ac.kr"), + new University("호서대학교", "hoseo.edu"), + new University("영진전문대학교", "yjc.ac.kr"), + new University("공주대학교", "kongju.ac.kr"), + new University("경성대학교", "ks.ac.kr"), + new University("신구대학교", "shingu.ac.kr"), + new University("한남대학교", "hannam.ac.kr"), + new University("울산대학교", "ulsan.ac.kr"), + new University("대림대학교", "daelim.ac.kr"), + new University("동서울대학교", "dsc.ac.kr"), + new University("청주대학교", "cju.ac.kr"), + new University("경남정보대학교", "kit.ac.kr"), + new University("동양미래대학교", "dongyang.ac.kr"), + new University("대구보건대학교", "dhc.ac.kr"), + new University("연성대학교", "yeonsung.ac.kr"), + new University("전주대학교", "jj.ac.kr"), + new University("서일대학교", "seoil.ac.kr"), + new University("인덕대학교", "induk.ac.kr"), + new University("고려사이버대학교", "cuk.edu"), + new University("영남이공대학교", "ync.ac.kr"), + new University("장안대학교", "jangan.ac.kr"), + new University("순천향대학교", "sch.ac.kr"), + new University("백석문화대학교", "bscu.ac.kr"), + new University("계명문화대학교", "kmcu.ac.kr"), + new University("경남대학교", "kyungnam.ac.kr"), + new University("남서울대학교", "nsu.ac.kr"), + new University("오산대학교", "osan.ac.kr"), + new University("세종사이버대학교", "sjcu.ac.kr"), + new University("제주대학교", "jejunu.ac.kr"), + new University("경복대학교", "kbu.ac.kr"), + new University("마산대학교", "masan.ac.kr"), + new University("수원대학교", "suwon.ac.kr"), + new University("상지대학교", "sangji.ac.kr"), + new University("수원과학대학교", "ssc.ac.kr"), + new University("동서대학교", "dongseo.ac.kr"), + new University("대전보건대학교", "hit.ac.kr"), + new University("선문대학교", "sunmoon.ac.kr"), + new University("유한대학교", "yuhan.ac.kr"), + new University("경인여자대학교", "kic.ac.kr"), + new University("배재대학교", "pcu.ac.kr"), + new University("서영대학교", "seoyoung.ac.kr"), + new University("우송대학교", "wsu.ac.kr"), + new University("대전대학교", "dju.ac.kr"), + new University("중부대학교", "jmail.ac.kr"), + new University("한국교통대학교", "ut.ac.kr"), + new University("인제대학교", "inje.ac.kr"), + new University("동의과학대학교", "dit.ac.kr"), + new University("한밭대학교", "hanbat.ac.kr"), + new University("한성대학교", "hansung.ac.kr"), + new University("삼육대학교", "syu.ac.kr"), + new University("한국항공대학교", "kau.ac.kr"), + new University("서울예술대학교", "seoularts.ac.kr"), + new University("부산대학교", "pusan.ac.kr"), + new University("상명대학교", "smu.ac.kr") + ); + + universityRepository.saveAll(universities); + log.info("Successfully initialized {} universities based on the original enum data.", universities.size()); + } + + @Transactional + public void clearUniversities() { + universityRepository.deleteAll(); + log.info("All universities have been deleted."); + } +} diff --git a/src/main/java/com/tools/seoultech/timoproject/global/config/SwaggerConfig.java b/src/main/java/com/tools/seoultech/timoproject/global/config/SwaggerConfig.java index 1fac0454..b69e2f5a 100644 --- a/src/main/java/com/tools/seoultech/timoproject/global/config/SwaggerConfig.java +++ b/src/main/java/com/tools/seoultech/timoproject/global/config/SwaggerConfig.java @@ -70,7 +70,8 @@ public GroupedOpenApi groupedOpenApi() { "com.tools.seoultech.timoproject.auth", "com.tools.seoultech.timoproject.riot", "com.tools.seoultech.timoproject.notification", - "com.tools.seoultech.timoproject.review" + "com.tools.seoultech.timoproject.review", + "com.tools.seoultech.timoproject.auth", }; return GroupedOpenApi.builder() diff --git a/src/main/java/com/tools/seoultech/timoproject/global/constant/ErrorCode.java b/src/main/java/com/tools/seoultech/timoproject/global/constant/ErrorCode.java index 5d9e3ad6..c6bef11a 100644 --- a/src/main/java/com/tools/seoultech/timoproject/global/constant/ErrorCode.java +++ b/src/main/java/com/tools/seoultech/timoproject/global/constant/ErrorCode.java @@ -81,7 +81,11 @@ public enum ErrorCode { RIOT_ACCOUNT_INFO_NOT_FOUND(1003, HttpStatus.NOT_FOUND, "라이엇 계정 정보가 존재하지 않습니다."), INVALID_EMAIL_FORMAT(1004,HttpStatus.BAD_REQUEST, "유효하지 않은 이메일 형식입니다." ), VERIFICATION_SYNC_FAILED(1005, HttpStatus.INTERNAL_SERVER_ERROR, "인증 타입 동기화에 실패했습니다."), INVALID_VERIFICATION_TYPE(1006, HttpStatus.BAD_REQUEST, "유효하지 않은 인증 타입입니다."), - RIOT_ACCOUNT_NOT_FOUND(1006, HttpStatus.NOT_FOUND, "라이엇 계정 정보가 존재하지 않습니다."),; + RIOT_ACCOUNT_NOT_FOUND(1006, HttpStatus.NOT_FOUND, "라이엇 계정 정보가 존재하지 않습니다."), + NOT_FOUND_UNIV(1007, HttpStatus.NOT_FOUND, "존재하지 않는 학교입니다."), + NOT_FOUND_CERTIFY_REQUEST(1008, HttpStatus.NOT_FOUND, "인증 요청을 찾을 수 없습니다."), + EXPIRED_VERIFICATION_CODE(1009, HttpStatus.INTERNAL_SERVER_ERROR, "인증 코드가 만료되었습니다."), + INVALID_VERIFICATION_CODE(1010, HttpStatus.BAD_REQUEST, "유효하지 않은 인증 코드입니다."); private final int code; diff --git a/src/main/java/com/tools/seoultech/timoproject/member/domain/entity/Member.java b/src/main/java/com/tools/seoultech/timoproject/member/domain/entity/Member.java index ea08ef35..6d202db3 100644 --- a/src/main/java/com/tools/seoultech/timoproject/member/domain/entity/Member.java +++ b/src/main/java/com/tools/seoultech/timoproject/member/domain/entity/Member.java @@ -104,4 +104,12 @@ public boolean shouldReceiveEmailNotification() { public String getEmailForNotification() { return shouldReceiveEmailNotification() ? notificationEmail : null; } + + public void updateCertifiedUnivInfo(CertifiedUnivInfo certifiedUnivInfo) { + this.certifiedUnivInfo = certifiedUnivInfo; + } + + public void clearCertifiedUnivInfo() { + this.certifiedUnivInfo = new CertifiedUnivInfo(); + } } \ No newline at end of file diff --git a/src/main/java/com/tools/seoultech/timoproject/member/service/MemberService.java b/src/main/java/com/tools/seoultech/timoproject/member/service/MemberService.java index 49616138..294c8bc2 100644 --- a/src/main/java/com/tools/seoultech/timoproject/member/service/MemberService.java +++ b/src/main/java/com/tools/seoultech/timoproject/member/service/MemberService.java @@ -7,6 +7,7 @@ import com.tools.seoultech.timoproject.member.domain.entity.Member; import com.tools.seoultech.timoproject.member.dto.NotificationEmailResponse; import com.tools.seoultech.timoproject.member.dto.UpdateMemberInfoRequest; +import jakarta.validation.constraints.Email; public interface MemberService { @@ -36,4 +37,7 @@ public interface MemberService { void updateNotificationEmail(Long memberId, String notificationEmail); NotificationEmailResponse getNotificationEmailSettings(Long memberId); Member save(Member member); + + boolean isUnivEmailCertified(@Email String s); + } diff --git a/src/main/java/com/tools/seoultech/timoproject/member/service/impl/MemberServiceImpl.java b/src/main/java/com/tools/seoultech/timoproject/member/service/impl/MemberServiceImpl.java index bfcf2385..b0a72445 100644 --- a/src/main/java/com/tools/seoultech/timoproject/member/service/impl/MemberServiceImpl.java +++ b/src/main/java/com/tools/seoultech/timoproject/member/service/impl/MemberServiceImpl.java @@ -204,6 +204,14 @@ public Member save(Member member) { return memberRepository.save(member); } + @Override + public boolean isUnivEmailCertified(String univEmail) { + if (univEmail == null || univEmail.isBlank()) { + return false; + } + return memberRepository.existsByCertifiedUnivInfo_UnivCertifiedEmail(univEmail); + } + @Override @Transactional public void updateVerificationType(Long memberId, String verificationType) { diff --git a/src/main/resources/db/migration/V1.6__create_university_table.sql b/src/main/resources/db/migration/V1.6__create_university_table.sql new file mode 100644 index 00000000..e2d99c38 --- /dev/null +++ b/src/main/resources/db/migration/V1.6__create_university_table.sql @@ -0,0 +1,6 @@ +CREATE TABLE university +( + id BIGINT AUTO_INCREMENT PRIMARY KEY, + name VARCHAR(255) NOT NULL UNIQUE, + domain VARCHAR(255) NOT NULL +); \ No newline at end of file diff --git a/src/main/resources/db/migration/V1.7__create_univ_verification_table.sql b/src/main/resources/db/migration/V1.7__create_univ_verification_table.sql new file mode 100644 index 00000000..f7a76adc --- /dev/null +++ b/src/main/resources/db/migration/V1.7__create_univ_verification_table.sql @@ -0,0 +1,8 @@ +CREATE TABLE univ_verification +( + email VARCHAR(255) PRIMARY KEY, + univ_name VARCHAR(255) NOT NULL, + code VARCHAR(255) NOT NULL, + is_verified BOOLEAN NOT NULL DEFAULT FALSE, + expires_at DATETIME(6) NOT NULL +); \ No newline at end of file