Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
@@ -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);
}
Original file line number Diff line number Diff line change
@@ -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
Expand All @@ -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);
}
}
Original file line number Diff line number Diff line change
@@ -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;
Expand All @@ -11,73 +15,98 @@
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
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<String, Object> 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<String, Object> 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<String, Object> 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<String, Object> 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<String, Object> 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));
}

}
Original file line number Diff line number Diff line change
@@ -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<String> initializeUniversities() {
universitySetupService.initializeUniversities();
return ResponseEntity.ok("University data initialization triggered.");
}

@PostMapping("/universities/clear")
public ResponseEntity<String> clearUniversities() {
universitySetupService.clearUniversities();
return ResponseEntity.ok("University data cleared.");
}
}
Original file line number Diff line number Diff line change
@@ -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<APIErrorResponse> checkUniv(@Valid @RequestBody UnivRequestDTO univRequestDto) {
univApiFacade.checkUniv(univRequestDto);
return ResponseEntity.status(HttpStatus.OK).body(APIErrorResponse.of(true, ErrorCode.OK));
}

@PostMapping("/request")
public ResponseEntity<APIErrorResponse> 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<APIErrorResponse> 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<Void> deleteUniv(@CurrentMemberId Long memberId) {
univApiFacade.deleteCertifiedUniv(memberId);
return ResponseEntity.noContent().build();
}
}
Loading