Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

# 57 회원 탈퇴 로직 #64

Merged
merged 12 commits into from
Oct 24, 2023
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package team.msg.domain.admin.exception

import team.msg.domain.admin.exception.constant.AdminErrorCode
import team.msg.global.error.exception.BitgouelException

class AdminNotFoundException(
message: String
) : BitgouelException(message, AdminErrorCode.ADMIN_NOT_FOUND.status)
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package team.msg.domain.admin.exception.constant

enum class AdminErrorCode(
val status: Int
) {
ADMIN_NOT_FOUND(404)
}
Original file line number Diff line number Diff line change
@@ -1,13 +1,12 @@
package team.msg.domain.auth.exception.constant

enum class AuthErrorCode(
val message: String,
val status: Int
) {
ALREADY_EXIST_EMAIL("이미 가입된 이메일입니다.", 409),
ALREADY_EXIST_PHONE_NUMBER("이미 가입된 전화번호입니다.", 409),
MISMATCH_PASSWORD("일치하지 않는 비밀번호입니다.", 401),
UNAPPROVED_USER("아직 회원가입 대기 상태입니다.", 403),
INVALID_TOKEN("유효하지 않은 토큰입니다.", 401),
REFRESH_TOKEN_NOT_FOUND("존재하지 않는 리프레시 토큰입니다.", 404)
ALREADY_EXIST_EMAIL(409),
ALREADY_EXIST_PHONE_NUMBER(409),
MISMATCH_PASSWORD(401),
UNAPPROVED_USER(403),
INVALID_TOKEN(401),
REFRESH_TOKEN_NOT_FOUND(404)
}
Original file line number Diff line number Diff line change
Expand Up @@ -74,4 +74,10 @@ class AuthController(
authService.logout(refreshToken)
return ResponseEntity.status(HttpStatus.NO_CONTENT).build()
}

@DeleteMapping("/withdraw")
fun withdraw(): ResponseEntity<Void> {
authService.withdraw()
return ResponseEntity.status(HttpStatus.NO_CONTENT).build()
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,4 +12,5 @@ interface AuthService {
fun login(request: LoginRequest): TokenResponse
fun reissueToken(refreshToken: String): TokenResponse
fun logout(refreshToken: String)
fun withdraw()
}
Original file line number Diff line number Diff line change
@@ -1,17 +1,13 @@
package team.msg.domain.auth.service

import org.springframework.context.ApplicationEventPublisher
import org.springframework.data.repository.findByIdOrNull
import org.springframework.stereotype.Service
import org.springframework.transaction.annotation.Transactional
import team.msg.common.enum.ApproveStatus
import team.msg.common.util.SecurityUtil
import team.msg.common.util.UserUtil
import team.msg.domain.auth.exception.AlreadyExistEmailException
import team.msg.domain.auth.exception.AlreadyExistPhoneNumberException
import team.msg.domain.auth.exception.InvalidRefreshTokenException
import team.msg.domain.auth.exception.MisMatchPasswordException
import team.msg.domain.auth.exception.RefreshTokenNotFoundException
import team.msg.domain.auth.exception.UnApprovedUserException
import team.msg.domain.auth.exception.*
import team.msg.domain.auth.presentation.data.request.*
import team.msg.domain.auth.presentation.data.response.TokenResponse
import team.msg.domain.auth.repository.RefreshTokenRepository
Expand All @@ -33,6 +29,7 @@ import team.msg.domain.student.repository.StudentRepository
import team.msg.domain.teacher.model.Teacher
import team.msg.domain.teacher.repository.TeacherRepository
import team.msg.domain.user.enums.Authority
import team.msg.domain.user.event.WithdrawUserEvent
import team.msg.domain.user.exception.UserNotFoundException
import team.msg.domain.user.model.User
import team.msg.domain.user.repository.UserRepository
Expand All @@ -54,7 +51,8 @@ class AuthServiceImpl(
private val jwtTokenGenerator: JwtTokenGenerator,
private val jwtTokenParser: JwtTokenParser,
private val refreshTokenRepository: RefreshTokenRepository,
private val userUtil: UserUtil
private val userUtil: UserUtil,
private val applicationEventPublisher: ApplicationEventPublisher
) : AuthService {

/**
Expand Down Expand Up @@ -213,6 +211,7 @@ class AuthServiceImpl(
val user = userRepository.findByIdOrNull(token.userId)
?: throw UserNotFoundException("존재하지 않는 유저입니다. info : [ userId = ${token.userId} ]")

refreshTokenRepository.deleteById(refreshToken)
return jwtTokenGenerator.generateToken(user.id, user.authority)
}

Expand All @@ -236,6 +235,18 @@ class AuthServiceImpl(
refreshTokenRepository.delete(token)
}

/**
* 회원탈퇴를 처리하는 메서드입니다.
*/
@Transactional(rollbackFor = [Exception::class])
override fun withdraw() {
val user = userUtil.queryCurrentUser()

applicationEventPublisher.publishEvent(WithdrawUserEvent(user))

userRepository.delete(user)
}

/**
* 유저 생성과 검증을 처리하는 private 메서드입니다.
* @param 유저 생성 및 검증하기 위한 email, name, phoneNumber, password, authority 입니다.
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package team.msg.domain.bbozzak.exception

import team.msg.domain.bbozzak.exception.constant.BbozzakErrorCode
import team.msg.global.error.exception.BitgouelException

class BbozzakNotFoundException(
message: String
) : BitgouelException(message, BbozzakErrorCode.BBOZZAK_NOT_FOUND.status)
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package team.msg.domain.bbozzak.exception.constant

enum class BbozzakErrorCode(
val status: Int
) {
BBOZZAK_NOT_FOUND(404)
}
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
package team.msg.domain.club.exception.constant

enum class ClubErrorCode(
val message: String,
val status: Int
) {
CLUB_NOT_FOUND("존재하지 않는 동아리입니다.", 404)
CLUB_NOT_FOUND(404)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package team.msg.domain.company.exception

import team.msg.domain.company.exception.constant.CompanyErrorCode
import team.msg.global.error.exception.BitgouelException

class CompanyNotFoundException(
message: String
) : BitgouelException(message, CompanyErrorCode.COMPANY_NOT_FOUND.status)
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package team.msg.domain.company.exception.constant

enum class CompanyErrorCode(
val status: Int
) {
COMPANY_NOT_FOUND(404)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package team.msg.domain.government

import team.msg.domain.government.exception.constant.GovernmentErrorCode
import team.msg.global.error.exception.BitgouelException

class GovernmentNotFoundException(
message: String
) : BitgouelException(message, GovernmentErrorCode.GOVERNMENT_NOT_FOUND.status)
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package team.msg.domain.government.exception.constant

enum class GovernmentErrorCode(
val status: Int
) {
GOVERNMENT_NOT_FOUND(404)
}
Original file line number Diff line number Diff line change
@@ -1,12 +1,9 @@
package team.msg.domain.lecture.exception.constant

enum class LectureErrorCode(
val message: String,
val status: Int
){
INVALID_LECTURE_TYPE("유효하지 않은 강의 구분입니다.", 400),

LECTURE_NOT_FOUND("존재하지 않는 강의입니다.", 404),

ALREADY_APPROVED_LECTURE("이미 개설 신청이 승인된 강의입니다.",409)
INVALID_LECTURE_TYPE(400),
LECTURE_NOT_FOUND(404),
ALREADY_APPROVED_LECTURE(409)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package team.msg.domain.professor.exception

import team.msg.domain.professor.exception.constant.ProfessorErrorCode
import team.msg.global.error.exception.BitgouelException

class ProfessorNotFoundException(
message: String
) : BitgouelException(message, ProfessorErrorCode.PROFESSOR_NOT_FOUND.status)
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package team.msg.domain.professor.exception.constant

enum class ProfessorErrorCode(
val status: Int
) {
PROFESSOR_NOT_FOUND(404)
}
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
package team.msg.domain.school.exception.constant

enum class SchoolErrorCode(
val message: String,
val status: Int
) {
SCHOOL_NOT_FOUND("이미 가입된 정보입니다.", 403)
SCHOOL_NOT_FOUND(403)
}
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
package team.msg.domain.student.exception.constant

enum class StudentActivityErrorCode(
val message: String,
val status: Int
) {
STUDENT_ACTIVITY_NOT_FOUND("학생 활동을 찾을 수 없습니다", 404)
STUDENT_ACTIVITY_NOT_FOUND(404)
}
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
package team.msg.domain.student.exception.constant

enum class StudentErrorCode(
val message: String,
val status: Int
) {
STUDENT_NOT_FOUND("학생을 찾을 수 없습니다.", 404)
STUDENT_NOT_FOUND(404)
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,5 +5,4 @@ import team.msg.global.error.exception.BitgouelException

class TeacherNotFoundException(
message: String
) : BitgouelException(message, TeacherErrorCode.TEACHER_NOT_FOUND.status) {
}
) : BitgouelException(message, TeacherErrorCode.TEACHER_NOT_FOUND.status)
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
package team.msg.domain.teacher.exception.constant

enum class TeacherErrorCode(
val message: String,
val status: Int
) {
TEACHER_NOT_FOUND("취업 동아리 선생님을 찾을 수 없습니다.", 404)
TEACHER_NOT_FOUND(404)
}
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
package team.msg.domain.user.exception.constant

enum class UserErrorCode(
val message: String,
val status: Int
) {
USER_NOT_FOUND("존재하지 않는 유저입니다.", 404)
USER_NOT_FOUND(404)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
package team.msg.domain.user.handler

import org.springframework.stereotype.Component
import org.springframework.transaction.event.TransactionPhase
import org.springframework.transaction.event.TransactionalEventListener
import team.msg.domain.admin.exception.AdminNotFoundException
import team.msg.domain.admin.repository.AdminRepository
import team.msg.domain.auth.exception.UnApprovedUserException
import team.msg.domain.bbozzak.exception.BbozzakNotFoundException
import team.msg.domain.bbozzak.repository.BbozzakRepository
import team.msg.domain.company.exception.CompanyNotFoundException
import team.msg.domain.company.repository.CompanyInstructorRepository
import team.msg.domain.government.GovernmentNotFoundException
import team.msg.domain.government.repository.GovernmentRepository
import team.msg.domain.lecture.repository.RegisteredLectureRepository
import team.msg.domain.professor.exception.ProfessorNotFoundException
import team.msg.domain.professor.repository.ProfessorRepository
import team.msg.domain.student.exception.StudentNotFoundException
import team.msg.domain.student.repository.StudentActivityRepository
import team.msg.domain.student.repository.StudentRepository
import team.msg.domain.teacher.exception.TeacherNotFoundException
import team.msg.domain.teacher.repository.TeacherRepository
import team.msg.domain.user.enums.Authority.*
import team.msg.domain.user.event.WithdrawUserEvent
import team.msg.domain.user.repository.UserRepository

@Component
class UserEventHandler(
private val userRepository: UserRepository,
private val studentRepository: StudentRepository,
private val studentActivityRepository: StudentActivityRepository,
private val bbozzakRepository: BbozzakRepository,
private val teacherRepository: TeacherRepository,
private val professorRepository: ProfessorRepository,
private val companyInstructorRepository: CompanyInstructorRepository,
private val governmentRepository: GovernmentRepository,
private val registeredLectureRepository: RegisteredLectureRepository,
private val adminRepository: AdminRepository
) {

/**
* User 의 delete 이벤트가 발행되면 User 를 삭제하는 핸들입니다.
* @param user delete 이벤트
*/
@TransactionalEventListener(phase = TransactionPhase.BEFORE_COMMIT)
fun withdrawUserHandler(event: WithdrawUserEvent) {
val user = event.user

when (user.authority) {
ROLE_STUDENT -> {
val student = studentRepository.findByUser(user)
?: throw StudentNotFoundException("존재하지 않는 학생 입니다. info : [ userId = ${user.id} ]")
val studentActivity = studentActivityRepository.findAllByStudent(student)
val registeredLecture = registeredLectureRepository.findAllByStudent(student)

studentActivityRepository.deleteAll(studentActivity)
registeredLectureRepository.deleteAll(registeredLecture)
studentRepository.delete(student)
}
ROLE_ADMIN -> {
val admin = adminRepository.findByUser(user)
?: throw AdminNotFoundException("존재하지 않는 어드민 입니다. info : [ userId = ${user.id} ]")

adminRepository.delete(admin)
}
ROLE_BBOZZAK -> {
val bbozzak = bbozzakRepository.findByUser(user)
?: throw BbozzakNotFoundException("존재하지 않는 뽀짝샘 입니다. info : [ userId = ${user.id} ]")

bbozzakRepository.delete(bbozzak)
}
ROLE_TEACHER -> {
val teacher = teacherRepository.findByUser(user)
?: throw TeacherNotFoundException("존재하지 않는 취동샘 입니다. info : [ userId = ${user.id} ]")

teacherRepository.delete(teacher)
}
ROLE_PROFESSOR -> {
val professor = professorRepository.findByUser(user)
?: throw ProfessorNotFoundException("존재하지 않는 대학 교수 입니다. info : [ userId = ${user.id} ]")

professorRepository.delete(professor)
}
ROLE_COMPANY_INSTRUCTOR -> {
val companyInstructor = companyInstructorRepository.findByUser(user)
?: throw CompanyNotFoundException("존재하지 않는 기업 강사 입니다. info : [ userId = ${user.id} ]")

companyInstructorRepository.delete(companyInstructor)
}
ROLE_GOVERNMENT -> {
val government = governmentRepository.findByUser(user)
?: throw GovernmentNotFoundException("존재하지 않는 유관 기관 입니다. info : [ userId = ${user.id} ]")

governmentRepository.delete(government)
}

else -> throw UnApprovedUserException("회원가입 승인 대기 중인 유저입니다. info : [ userId = ${user.id} ]")
}
}
}
Loading