-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* feat: 관리자 계정들의 id 자동 생성되도록 수정 #170 * feat: super admin 어노테이션 구현 #170 * feat: 관리자 계정의 어노테이션 구현 #170 * feat: 관리자 단일 등록 api 구현 #170 * feat: Admin & SuperAdmin annotation 등록 #170 * feat: config 폴더 최신화 #170 * feat: 파일을 이용한 교직원 등록 api 구현 #170 * feat: 교직원 탈퇴 api 구현 #170 * feat: 로그인 할 때 deleted 된 사용자인지 검증하는 로직 추가 #179 * feat: 이메일 및 전화번호 중복확인 api 삭제 #170 * feat: 회원가입 및 이메일 인증시 탈퇴한 회원인지 확인하는 로직 추가 #170 * feat: 로그인 관련 회원을 찾지 못할 때의 문구 수정 #170 * feat: 교직원 단일/다중 등록 시 deleted 된 회원인지 검증하는 로직 추가 #170 * refactor: 관리자 등록 및 삭제 api의 컨트롤러 명 수정 #170 * refactor: 관리자 권한 관련 컨트롤러 명 수정 #170 * feat: 관리자 등록/삭제 관련 api test 코드 작성 및 rest docs 작성 #170 * refactor: 코드리뷰 반영 - soft delete 에 해당하는 SQLRestriction 사용하기 - 조회를 뜻하는 queryService에서 commandService로 서비스명 수정 #170 * refactor: 코드리뷰 반영 - setter 삭제 #170
- Loading branch information
Showing
28 changed files
with
645 additions
and
132 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
54 changes: 54 additions & 0 deletions
54
backend/src/main/java/sw_css/admin/auth/api/AdminAuthController.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,54 @@ | ||
package sw_css.admin.auth.api; | ||
|
||
import jakarta.validation.Valid; | ||
import java.net.URI; | ||
import lombok.RequiredArgsConstructor; | ||
import org.springframework.http.ResponseEntity; | ||
import org.springframework.validation.annotation.Validated; | ||
import org.springframework.web.bind.annotation.DeleteMapping; | ||
import org.springframework.web.bind.annotation.PostMapping; | ||
import org.springframework.web.bind.annotation.RequestBody; | ||
import org.springframework.web.bind.annotation.RequestMapping; | ||
import org.springframework.web.bind.annotation.RequestPart; | ||
import org.springframework.web.bind.annotation.RestController; | ||
import org.springframework.web.multipart.MultipartFile; | ||
import sw_css.admin.auth.application.AdminAuthCommandService; | ||
import sw_css.admin.auth.application.dto.request.DeleteFacultyRequest; | ||
import sw_css.admin.auth.application.dto.request.RegisterFacultyRequest; | ||
import sw_css.member.domain.FacultyMember; | ||
import sw_css.utils.annotation.Admin; | ||
import sw_css.utils.annotation.SuperAdmin; | ||
|
||
@Validated | ||
@RequestMapping("/admin/auth") | ||
@RestController | ||
@RequiredArgsConstructor | ||
public class AdminAuthController { | ||
|
||
private final AdminAuthCommandService adminAuthCommandService; | ||
|
||
@PostMapping | ||
public ResponseEntity<Void> registerFaculty( | ||
@Admin FacultyMember facultyMember, | ||
@RequestBody @Valid RegisterFacultyRequest request) { | ||
Long memberId = adminAuthCommandService.registerFaculty(request); | ||
return ResponseEntity.created(URI.create("/members/" + memberId)).build(); | ||
} | ||
|
||
@PostMapping("/files") | ||
public ResponseEntity<Void> registerFaculties( | ||
@Admin FacultyMember facultyMember, | ||
@RequestPart(value = "file") final MultipartFile file) { | ||
adminAuthCommandService.registerFaculties(file); | ||
return ResponseEntity.created(URI.create("/admin/faculties")).build(); | ||
} | ||
|
||
@DeleteMapping() | ||
public ResponseEntity<Void> deleteFaculty( | ||
@SuperAdmin FacultyMember facultyMember, | ||
@RequestBody @Valid DeleteFacultyRequest request) { | ||
adminAuthCommandService.deleteFaculty(request.member_id()); | ||
return ResponseEntity.noContent().build(); | ||
} | ||
|
||
} |
156 changes: 156 additions & 0 deletions
156
backend/src/main/java/sw_css/admin/auth/application/AdminAuthCommandService.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,156 @@ | ||
package sw_css.admin.auth.application; | ||
|
||
import java.io.IOException; | ||
import java.util.ArrayList; | ||
import java.util.List; | ||
import java.util.regex.Pattern; | ||
import java.util.stream.Collectors; | ||
import lombok.RequiredArgsConstructor; | ||
import org.apache.commons.io.FilenameUtils; | ||
import org.apache.poi.hssf.usermodel.HSSFWorkbook; | ||
import org.apache.poi.ss.usermodel.Row; | ||
import org.apache.poi.ss.usermodel.Sheet; | ||
import org.apache.poi.ss.usermodel.Workbook; | ||
import org.apache.poi.xssf.usermodel.XSSFWorkbook; | ||
import org.springframework.beans.factory.annotation.Value; | ||
import org.springframework.stereotype.Service; | ||
import org.springframework.transaction.annotation.Transactional; | ||
import org.springframework.web.multipart.MultipartFile; | ||
import sw_css.admin.auth.application.dto.request.RegisterFacultyRequest; | ||
import sw_css.admin.auth.exception.AdminAuthException; | ||
import sw_css.admin.auth.exception.AdminAuthExceptionType; | ||
import sw_css.auth.application.AuthCheckDuplicateService; | ||
import sw_css.member.domain.FacultyMember; | ||
import sw_css.member.domain.Member; | ||
import sw_css.member.domain.embedded.EmailAddress; | ||
import sw_css.member.domain.embedded.Password; | ||
import sw_css.member.domain.embedded.RealName; | ||
import sw_css.member.domain.repository.FacultyMemberRepository; | ||
import sw_css.member.domain.repository.MemberRepository; | ||
|
||
@Service | ||
@RequiredArgsConstructor | ||
public class AdminAuthCommandService { | ||
|
||
private final MemberRepository memberRepository; | ||
private final FacultyMemberRepository facultyMemberRepository; | ||
private final AuthCheckDuplicateService authCheckDuplicateService; | ||
|
||
@Value("${password.admin}") | ||
private String password; | ||
|
||
@Transactional | ||
public Long registerFaculty(RegisterFacultyRequest request) { | ||
validateDuplicateEmail(request.email()); | ||
|
||
final String encodedPassword = Password.encode(password); | ||
|
||
final long memberId = memberRepository.save(request.toMember(encodedPassword)).getId(); | ||
facultyMemberRepository.save(request.toFacultyMember(memberId, encodedPassword)); | ||
|
||
return memberId; | ||
} | ||
|
||
public void registerFaculties(MultipartFile file) { | ||
final String extension = FilenameUtils.getExtension(file.getOriginalFilename()); | ||
if (extension == null || !(extension.equals("xlsx") || extension.equals("xls"))) { | ||
throw new AdminAuthException(AdminAuthExceptionType.NO_MATCH_EXTENSION); | ||
} | ||
|
||
final String encodedPassword = Password.encode(password); | ||
final List<Integer> failedData = new ArrayList<>(); | ||
|
||
final Workbook workbook = generateWorkbook(file, extension); | ||
|
||
final Sheet worksheet = workbook.getSheetAt(0); | ||
for (int i = 1; i < worksheet.getPhysicalNumberOfRows(); i++) { | ||
final Row row = worksheet.getRow(i); | ||
|
||
final String email = row.getCell(0).getStringCellValue(); | ||
final String name = row.getCell(1).getStringCellValue(); | ||
|
||
if (isInvalidInput(email, name)) { | ||
failedData.add(i + 1); | ||
continue; | ||
} | ||
|
||
saveFaculty(email, name, encodedPassword); | ||
} | ||
|
||
checkFailedData(failedData); | ||
} | ||
|
||
@Transactional | ||
public void deleteFaculty(Long memberId) { | ||
FacultyMember facultyMember = facultyMemberRepository.findById(memberId) | ||
.orElseThrow(() -> new AdminAuthException(AdminAuthExceptionType.MEMBER_NOT_FOUND)); | ||
|
||
Member member = facultyMember.getMember(); | ||
|
||
checkIsMemberDeleted(member); | ||
|
||
member.setDeleted(true); | ||
memberRepository.save(member); | ||
} | ||
|
||
private Workbook generateWorkbook(final MultipartFile file, String extension) { | ||
try { | ||
if (extension.equals("xlsx")) { | ||
return new XSSFWorkbook(file.getInputStream()); | ||
} | ||
return new HSSFWorkbook(file.getInputStream()); | ||
} catch (final IOException exception) { | ||
throw new AdminAuthException(AdminAuthExceptionType.CANNOT_OPEN_FILE); | ||
} | ||
} | ||
|
||
private void saveFaculty(final String email, final String name, final String password) { | ||
Member member = new Member(email, name, password, "01000000000", false); | ||
|
||
final Member savedMember = memberRepository.save(member); | ||
|
||
FacultyMember facultyMember = new FacultyMember(null, savedMember); | ||
facultyMemberRepository.save(facultyMember); | ||
} | ||
|
||
private void validateDuplicateEmail(String email) { | ||
if (authCheckDuplicateService.isDuplicateEmail(email)) { | ||
throw new AdminAuthException(AdminAuthExceptionType.MEMBER_EMAIL_DUPLICATE); | ||
} | ||
} | ||
|
||
private boolean isDuplicateEmail(String email) { | ||
return authCheckDuplicateService.isDuplicateEmail(email); | ||
} | ||
|
||
private boolean isInvalidInput(final String email, final String name) { | ||
if (Pattern.matches(EmailAddress.EMAIL_ADDRESS_REGEX, email) && | ||
Pattern.matches(RealName.NAME_REGEX, name) && !isDuplicateEmail(email)) { | ||
return false; | ||
} | ||
return true; | ||
} | ||
|
||
private void checkFailedData(final List<Integer> failedData) { | ||
if (failedData.isEmpty()) { | ||
return; | ||
} | ||
|
||
String ids = failedData.stream() | ||
.map(Object::toString) | ||
.collect(Collectors.joining(", ")); | ||
AdminAuthExceptionType exceptionType = AdminAuthExceptionType.FAILED_REGISTER_FACULTY; | ||
exceptionType.setErrorMessage(ids + "번째 줄의 관리자를 등록하는데 실패했습니다."); | ||
|
||
throw new AdminAuthException(exceptionType); | ||
} | ||
|
||
private void checkIsMemberDeleted(final Member member) { | ||
if (!member.isDeleted()) { | ||
return; | ||
} | ||
throw new AdminAuthException(AdminAuthExceptionType.MEMBER_NOT_FOUND); | ||
|
||
} | ||
|
||
} |
6 changes: 6 additions & 0 deletions
6
backend/src/main/java/sw_css/admin/auth/application/dto/request/DeleteFacultyRequest.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
package sw_css.admin.auth.application.dto.request; | ||
|
||
public record DeleteFacultyRequest( | ||
Long member_id | ||
) { | ||
} |
29 changes: 29 additions & 0 deletions
29
backend/src/main/java/sw_css/admin/auth/application/dto/request/RegisterFacultyRequest.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,29 @@ | ||
package sw_css.admin.auth.application.dto.request; | ||
|
||
import jakarta.validation.constraints.Email; | ||
import jakarta.validation.constraints.Pattern; | ||
import sw_css.member.domain.FacultyMember; | ||
import sw_css.member.domain.Member; | ||
import sw_css.member.domain.embedded.EmailAddress; | ||
import sw_css.member.domain.embedded.RealName; | ||
|
||
public record RegisterFacultyRequest( | ||
@Email(message = "이메일 형식을 확인해주세요.") | ||
@Pattern(regexp = EmailAddress.EMAIL_ADDRESS_REGEX, message = EmailAddress.EMAIL_ADDRESS_INVALID) | ||
String email, | ||
@Pattern(regexp = RealName.NAME_REGEX, message = RealName.NAME_INVALID) | ||
String name) { | ||
|
||
public Member toMember(String password) { | ||
return new Member(email, name, password, "01000000000", false); | ||
} | ||
|
||
public Member toMember(Long memberId, String password) { | ||
return new Member(memberId, email, name, password, "01000000000", false); | ||
} | ||
|
||
public FacultyMember toFacultyMember(Long memberId, String password) { | ||
final Member member = toMember(memberId, password); | ||
return new FacultyMember(null, member); | ||
} | ||
} |
19 changes: 19 additions & 0 deletions
19
backend/src/main/java/sw_css/admin/auth/exception/AdminAuthException.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
package sw_css.admin.auth.exception; | ||
|
||
|
||
import sw_css.base.BaseException; | ||
import sw_css.base.BaseExceptionType; | ||
|
||
public class AdminAuthException extends BaseException { | ||
private final AdminAuthExceptionType adminAuthExceptionType; | ||
|
||
public AdminAuthException(final AdminAuthExceptionType exceptionType) { | ||
super(exceptionType.errorMessage()); | ||
this.adminAuthExceptionType = exceptionType; | ||
} | ||
|
||
@Override | ||
public BaseExceptionType exceptionType() { | ||
return adminAuthExceptionType; | ||
} | ||
} |
34 changes: 34 additions & 0 deletions
34
backend/src/main/java/sw_css/admin/auth/exception/AdminAuthExceptionType.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,34 @@ | ||
package sw_css.admin.auth.exception; | ||
|
||
import lombok.Setter; | ||
import org.springframework.http.HttpStatus; | ||
import sw_css.base.BaseExceptionType; | ||
|
||
public enum AdminAuthExceptionType implements BaseExceptionType { | ||
NO_MATCH_EXTENSION(HttpStatus.BAD_REQUEST, "파일 확장자가 올바르지 않습니다."), | ||
CANNOT_OPEN_FILE(HttpStatus.BAD_REQUEST, "파일을 열 수 없습니다."), | ||
FAILED_REGISTER_FACULTY(HttpStatus.BAD_REQUEST, ""), | ||
MEMBER_EMAIL_DUPLICATE(HttpStatus.CONFLICT, "이메일이 중복됩니다."), | ||
MEMBER_NOT_FOUND(HttpStatus.NOT_FOUND, "해당 회원을 찾을 수 없습니다."), | ||
; | ||
|
||
private final HttpStatus httpStatus; | ||
@Setter | ||
private String errorMessage; | ||
|
||
AdminAuthExceptionType(final HttpStatus httpStatus, final String errorMessage) { | ||
this.httpStatus = httpStatus; | ||
this.errorMessage = errorMessage; | ||
} | ||
|
||
@Override | ||
public HttpStatus httpStatus() { | ||
return httpStatus; | ||
} | ||
|
||
@Override | ||
public String errorMessage() { | ||
return errorMessage; | ||
} | ||
|
||
} |
Oops, something went wrong.