diff --git a/apis/src/main/kotlin/org/yapp/apis/auth/controller/AuthController.kt b/apis/src/main/kotlin/org/yapp/apis/auth/controller/AuthController.kt index 0bccabfc..b3b2b71c 100644 --- a/apis/src/main/kotlin/org/yapp/apis/auth/controller/AuthController.kt +++ b/apis/src/main/kotlin/org/yapp/apis/auth/controller/AuthController.kt @@ -5,10 +5,9 @@ import org.springframework.http.ResponseEntity import org.springframework.security.core.annotation.AuthenticationPrincipal import org.springframework.web.bind.annotation.* import org.yapp.apis.auth.dto.request.SocialLoginRequest -import org.yapp.apis.auth.dto.request.TermsAgreementRequest import org.yapp.apis.auth.dto.request.TokenRefreshRequest +import org.yapp.apis.auth.dto.request.WithdrawRequest import org.yapp.apis.auth.dto.response.AuthResponse -import org.yapp.apis.auth.dto.response.UserProfileResponse import org.yapp.apis.auth.usecase.AuthUseCase import java.util.* @@ -36,18 +35,12 @@ class AuthController( return ResponseEntity.noContent().build() } - @GetMapping("/me") - override fun getUserProfile(@AuthenticationPrincipal userId: UUID): ResponseEntity { - val userProfile = authUseCase.getUserProfile(userId) - return ResponseEntity.ok(userProfile) - } - - @PutMapping("/terms-agreement") - override fun updateTermsAgreement( + @DeleteMapping("/withdraw") + override fun withdraw( @AuthenticationPrincipal userId: UUID, - @Valid @RequestBody request: TermsAgreementRequest - ): ResponseEntity { - val userProfile = authUseCase.updateTermsAgreement(userId, request.validTermsAgreed()) - return ResponseEntity.ok(userProfile) + @Valid @RequestBody request: WithdrawRequest + ): ResponseEntity { + authUseCase.withdraw(userId, request) + return ResponseEntity.noContent().build() } } diff --git a/apis/src/main/kotlin/org/yapp/apis/auth/controller/AuthControllerApi.kt b/apis/src/main/kotlin/org/yapp/apis/auth/controller/AuthControllerApi.kt index 20ea6d73..c2ca97d0 100644 --- a/apis/src/main/kotlin/org/yapp/apis/auth/controller/AuthControllerApi.kt +++ b/apis/src/main/kotlin/org/yapp/apis/auth/controller/AuthControllerApi.kt @@ -9,15 +9,13 @@ import io.swagger.v3.oas.annotations.tags.Tag import jakarta.validation.Valid import org.springframework.http.ResponseEntity import org.springframework.security.core.annotation.AuthenticationPrincipal -import org.springframework.web.bind.annotation.GetMapping +import org.springframework.web.bind.annotation.DeleteMapping import org.springframework.web.bind.annotation.PostMapping -import org.springframework.web.bind.annotation.PutMapping import org.springframework.web.bind.annotation.RequestBody import org.yapp.apis.auth.dto.request.SocialLoginRequest -import org.yapp.apis.auth.dto.request.TermsAgreementRequest import org.yapp.apis.auth.dto.request.TokenRefreshRequest +import org.yapp.apis.auth.dto.request.WithdrawRequest import org.yapp.apis.auth.dto.response.AuthResponse -import org.yapp.apis.auth.dto.response.UserProfileResponse import org.yapp.globalutils.exception.ErrorResponse import java.util.* @@ -90,42 +88,28 @@ interface AuthControllerApi { @PostMapping("/signout") fun signOut(@AuthenticationPrincipal userId: UUID): ResponseEntity - @Operation(summary = "사용자 프로필 조회", description = "현재 로그인한 사용자의 프로필 정보를 조회합니다.") - @ApiResponses( - value = [ - ApiResponse( - responseCode = "200", - description = "사용자 프로필 조회 성공", - content = [Content(schema = Schema(implementation = UserProfileResponse::class))] - ), - ApiResponse( - responseCode = "404", - description = "사용자를 찾을 수 없음", - content = [Content(schema = Schema(implementation = ErrorResponse::class))] - ) - ] + @Operation( + summary = "회원 탈퇴", + description = "사용자 계정을 탈퇴합니다." ) - @GetMapping("/me") - fun getUserProfile(@AuthenticationPrincipal userId: UUID): ResponseEntity - - @Operation(summary = "약관 동의 상태 수정", description = "사용자의 약관 동의 상태를 업데이트합니다") @ApiResponses( value = [ + ApiResponse(responseCode = "204", description = "회원 탈퇴 성공"), ApiResponse( - responseCode = "200", - description = "약관 동의 상태 업데이트 성공", - content = [Content(schema = Schema(implementation = UserProfileResponse::class))] + responseCode = "400", + description = "잘못된 요청 또는 사용자를 찾을 수 없음", + content = [Content(schema = Schema(implementation = ErrorResponse::class))] ), ApiResponse( - responseCode = "404", - description = "사용자를 찾을 수 없음", + responseCode = "500", + description = "Apple or Kakao 서버 연결 해제 실패", content = [Content(schema = Schema(implementation = ErrorResponse::class))] ) ] ) - @PutMapping("/terms-agreement") - fun updateTermsAgreement( + @DeleteMapping("/withdraw") + fun withdraw( @AuthenticationPrincipal userId: UUID, - @Valid @RequestBody request: TermsAgreementRequest - ): ResponseEntity + @Valid @RequestBody request: WithdrawRequest + ): ResponseEntity } diff --git a/apis/src/main/kotlin/org/yapp/apis/auth/dto/request/GenerateTokenPairRequest.kt b/apis/src/main/kotlin/org/yapp/apis/auth/dto/request/GenerateTokenPairRequest.kt index 5eadfa8e..ac8059ac 100644 --- a/apis/src/main/kotlin/org/yapp/apis/auth/dto/request/GenerateTokenPairRequest.kt +++ b/apis/src/main/kotlin/org/yapp/apis/auth/dto/request/GenerateTokenPairRequest.kt @@ -2,8 +2,8 @@ package org.yapp.apis.auth.dto.request import io.swagger.v3.oas.annotations.media.Schema import jakarta.validation.constraints.NotNull -import org.yapp.apis.auth.dto.response.CreateUserResponse -import org.yapp.apis.auth.dto.response.UserAuthInfoResponse +import org.yapp.apis.user.dto.response.CreateUserResponse +import org.yapp.apis.user.dto.response.UserAuthInfoResponse import org.yapp.globalutils.auth.Role import java.util.UUID diff --git a/apis/src/main/kotlin/org/yapp/apis/auth/dto/request/SaveAppleRefreshTokenRequest.kt b/apis/src/main/kotlin/org/yapp/apis/auth/dto/request/SaveAppleRefreshTokenRequest.kt index 0e4e9273..800d4d9a 100644 --- a/apis/src/main/kotlin/org/yapp/apis/auth/dto/request/SaveAppleRefreshTokenRequest.kt +++ b/apis/src/main/kotlin/org/yapp/apis/auth/dto/request/SaveAppleRefreshTokenRequest.kt @@ -3,8 +3,8 @@ package org.yapp.apis.auth.dto.request import io.swagger.v3.oas.annotations.media.Schema import jakarta.validation.constraints.NotBlank import jakarta.validation.constraints.NotNull -import org.yapp.apis.auth.dto.response.CreateUserResponse -import org.yapp.apis.auth.strategy.AppleAuthCredentials +import org.yapp.apis.user.dto.response.CreateUserResponse +import org.yapp.apis.auth.strategy.signin.AppleAuthCredentials import java.util.UUID @Schema( diff --git a/apis/src/main/kotlin/org/yapp/apis/auth/dto/request/SocialLoginRequest.kt b/apis/src/main/kotlin/org/yapp/apis/auth/dto/request/SocialLoginRequest.kt index 61238588..a85658a0 100644 --- a/apis/src/main/kotlin/org/yapp/apis/auth/dto/request/SocialLoginRequest.kt +++ b/apis/src/main/kotlin/org/yapp/apis/auth/dto/request/SocialLoginRequest.kt @@ -2,11 +2,11 @@ package org.yapp.apis.auth.dto.request import io.swagger.v3.oas.annotations.media.Schema import jakarta.validation.constraints.NotBlank -import org.yapp.apis.auth.strategy.AppleAuthCredentials -import org.yapp.apis.auth.strategy.AuthCredentials -import org.yapp.apis.auth.strategy.KakaoAuthCredentials import org.yapp.apis.auth.exception.AuthErrorCode import org.yapp.apis.auth.exception.AuthException +import org.yapp.apis.auth.strategy.signin.AppleAuthCredentials +import org.yapp.apis.auth.strategy.signin.KakaoAuthCredentials +import org.yapp.apis.auth.strategy.signin.SignInCredentials import org.yapp.domain.user.ProviderType @Schema( @@ -41,7 +41,7 @@ data class SocialLoginRequest private constructor( fun validOauthToken(): String = oauthToken!! companion object { - fun toCredentials(request: SocialLoginRequest): AuthCredentials { + fun toCredentials(request: SocialLoginRequest): SignInCredentials { val provider = try { ProviderType.valueOf(request.validProviderType().uppercase()) } catch (e: IllegalArgumentException) { @@ -55,7 +55,10 @@ data class SocialLoginRequest private constructor( ProviderType.KAKAO -> KakaoAuthCredentials(request.validOauthToken()) ProviderType.APPLE -> { val authCode = request.authorizationCode - ?: throw AuthException(AuthErrorCode.INVALID_REQUEST, "Apple login requires an authorization code.") + ?: throw AuthException( + AuthErrorCode.INVALID_REQUEST, + "Apple login requires an authorization code." + ) AppleAuthCredentials(request.validOauthToken(), authCode) } } diff --git a/apis/src/main/kotlin/org/yapp/apis/auth/dto/request/WithdrawRequest.kt b/apis/src/main/kotlin/org/yapp/apis/auth/dto/request/WithdrawRequest.kt new file mode 100644 index 00000000..1cc1a228 --- /dev/null +++ b/apis/src/main/kotlin/org/yapp/apis/auth/dto/request/WithdrawRequest.kt @@ -0,0 +1,22 @@ +package org.yapp.apis.auth.dto.request + +import io.swagger.v3.oas.annotations.media.Schema +import jakarta.validation.constraints.NotBlank +import jakarta.validation.constraints.NotNull +import org.yapp.domain.user.ProviderType + +@Schema( + name = "WithdrawRequest", + description = "DTO for user withdrawal requests" +) +data class WithdrawRequest private constructor( + @Schema( + description = "Type of social login provider for withdrawal", + example = "APPLE", + required = true + ) + @field:NotNull(message = "Provider type is not-null") + val providerType: ProviderType? = null +) { + fun validProviderType(): ProviderType = providerType!! +} diff --git a/apis/src/main/kotlin/org/yapp/apis/auth/dto/request/WithdrawStrategyRequest.kt b/apis/src/main/kotlin/org/yapp/apis/auth/dto/request/WithdrawStrategyRequest.kt new file mode 100644 index 00000000..bd0f8837 --- /dev/null +++ b/apis/src/main/kotlin/org/yapp/apis/auth/dto/request/WithdrawStrategyRequest.kt @@ -0,0 +1,45 @@ +package org.yapp.apis.auth.dto.request + +import io.swagger.v3.oas.annotations.media.Schema +import org.yapp.apis.user.dto.response.WithdrawTargetUserResponse +import org.yapp.domain.user.ProviderType +import java.util.* + +@Schema(description = "회원 탈퇴 처리 시 내부적으로 사용되는 요청 DTO") +data class WithdrawStrategyRequest private constructor( + @Schema( + description = "사용자 고유 ID", + example = "123e4567-e89b-12d3-a456-426614174000", + ) + val userId: UUID, + + @Schema( + description = "소셜 로그인 제공자 타입", + example = "KAKAO", + ) + val providerType: ProviderType, + + @Schema( + description = "소셜 로그인 제공자로부터 발급받은 고유 ID", + example = "21412412412", + ) + val providerId: String, + + @Schema( + description = "Apple 로그인 시 발급받은 리프레시 토큰 (Apple 로그인 회원 탈퇴 시에만 필요)", + example = "r_abc123def456ghi789jkl0mnopqrstu", + required = false + ) + val appleRefreshToken: String? +) { + companion object { + fun from(response: WithdrawTargetUserResponse): WithdrawStrategyRequest { + return WithdrawStrategyRequest( + userId = response.id, + providerType = response.providerType, + providerId = response.providerId, + appleRefreshToken = response.appleRefreshToken + ) + } + } +} diff --git a/apis/src/main/kotlin/org/yapp/apis/auth/exception/AuthErrorCode.kt b/apis/src/main/kotlin/org/yapp/apis/auth/exception/AuthErrorCode.kt index 45bebdc4..41b3358e 100644 --- a/apis/src/main/kotlin/org/yapp/apis/auth/exception/AuthErrorCode.kt +++ b/apis/src/main/kotlin/org/yapp/apis/auth/exception/AuthErrorCode.kt @@ -20,6 +20,8 @@ enum class AuthErrorCode( USER_NOT_FOUND(HttpStatus.BAD_REQUEST, "AUTH_400_08", "사용자를 찾을 수 없습니다."), EMAIL_NOT_FOUND(HttpStatus.BAD_REQUEST, "AUTH_400_09", "이메일을 찾을 수 없습니다."), INVALID_APPLE_ID_TOKEN(HttpStatus.BAD_REQUEST, "AUTH_400_10", "유효하지 않은 Apple ID 토큰입니다."), + PROVIDER_TYPE_MISMATCH(HttpStatus.BAD_REQUEST, "AUTH_400_11", "요청된 공급자 타입이 실제 사용자의 공급자 타입과 일치하지 않습니다."), + APPLE_REFRESH_TOKEN_MISSING(HttpStatus.BAD_REQUEST, "AUTH_400_12", "Apple 사용자 탈퇴 시 리프레시 토큰이 누락되었습니다."), /* 401 UNAUTHORIZED */ INVALID_OAUTH_TOKEN(HttpStatus.UNAUTHORIZED, "AUTH_401_01", "잘못된 소셜 OAuth 토큰입니다."), diff --git a/apis/src/main/kotlin/org/yapp/apis/auth/manager/AppleApiManager.kt b/apis/src/main/kotlin/org/yapp/apis/auth/manager/AppleApiManager.kt index 1612c7f7..707302ff 100644 --- a/apis/src/main/kotlin/org/yapp/apis/auth/manager/AppleApiManager.kt +++ b/apis/src/main/kotlin/org/yapp/apis/auth/manager/AppleApiManager.kt @@ -35,4 +35,23 @@ class AppleApiManager( ) } } + + fun revokeToken(appleRefreshToken: String) { + val clientSecret = appleClientSecretGenerator.generateClientSecret() + + appleApi.revokeAppleToken( + clientId = properties.clientId, + clientSecret = clientSecret, + token = appleRefreshToken, + tokenTypeHint = "refresh_token" + ).onSuccess { + log.info { "Successfully revoked Apple token." } + }.onFailure { originalError -> + log.error(originalError) { "Failed to revoke Apple token." } + throw AuthException( + AuthErrorCode.OAUTH_SERVER_ERROR, + "Failed to revoke Apple token: ${originalError.message}" + ) + } + } } diff --git a/apis/src/main/kotlin/org/yapp/apis/auth/service/AppleAuthService.kt b/apis/src/main/kotlin/org/yapp/apis/auth/service/AppleAuthService.kt index b147b179..9b25fad6 100644 --- a/apis/src/main/kotlin/org/yapp/apis/auth/service/AppleAuthService.kt +++ b/apis/src/main/kotlin/org/yapp/apis/auth/service/AppleAuthService.kt @@ -1,28 +1,23 @@ package org.yapp.apis.auth.service -import jakarta.validation.Valid import org.springframework.stereotype.Service import org.springframework.validation.annotation.Validated -import org.yapp.apis.auth.dto.request.SaveAppleRefreshTokenRequest import org.yapp.apis.auth.exception.AuthErrorCode import org.yapp.apis.auth.exception.AuthException import org.yapp.apis.auth.manager.AppleApiManager -import org.yapp.domain.user.UserDomainService +import org.yapp.infra.external.oauth.apple.response.AppleTokenResponse @Service @Validated class AppleAuthService( private val appleApiManager: AppleApiManager, - private val userDomainService: UserDomainService ) { - fun saveAppleRefreshTokenIfMissing(@Valid request: SaveAppleRefreshTokenRequest) { - if (request.appleRefreshToken == null) { - val tokenResponse = appleApiManager.fetchAppleOauthTokens(request.validAuthorizationCode()) + fun fetchAppleOauthTokens(authorizationCode: String): AppleTokenResponse { + val tokenResponse = appleApiManager.fetchAppleOauthTokens(authorizationCode) - val refreshToken = tokenResponse.refreshToken - ?: throw AuthException(AuthErrorCode.MISSING_APPLE_REFRESH_TOKEN) + tokenResponse.refreshToken + ?: throw AuthException(AuthErrorCode.MISSING_APPLE_REFRESH_TOKEN) - userDomainService.updateAppleRefreshToken(request.validUserId(), refreshToken) - } + return tokenResponse } } diff --git a/apis/src/main/kotlin/org/yapp/apis/auth/service/AuthTokenService.kt b/apis/src/main/kotlin/org/yapp/apis/auth/service/AuthTokenService.kt index 4ee67f36..d18e7afd 100644 --- a/apis/src/main/kotlin/org/yapp/apis/auth/service/AuthTokenService.kt +++ b/apis/src/main/kotlin/org/yapp/apis/auth/service/AuthTokenService.kt @@ -32,16 +32,16 @@ class AuthTokenService( return TokenPairResponse.of(accessToken, refreshTokenResponse.refreshToken) } - fun validateAndGetUserIdFromRefreshToken(@Valid tokenRefreshRequest: TokenRefreshRequest): UserIdResponse { + fun validateAndGetUserIdFromRefreshToken(tokenRefreshRequest: TokenRefreshRequest): UserIdResponse { refreshTokenService.validateRefreshToken(tokenRefreshRequest.validRefreshToken()) return refreshTokenService.getUserIdByToken(tokenRefreshRequest) } - fun deleteTokenForReissue(@Valid tokenRefreshRequest: TokenRefreshRequest) { + fun deleteRefreshTokenForReissue(tokenRefreshRequest: TokenRefreshRequest) { refreshTokenService.deleteRefreshTokenByToken(tokenRefreshRequest.validRefreshToken()) } - fun deleteTokenForSignOut(@Valid deleteTokenRequest: DeleteTokenRequest) { + fun deleteRefreshTokenForSignOutOrWithdraw(@Valid deleteTokenRequest: DeleteTokenRequest) { refreshTokenService.deleteRefreshTokenByToken(deleteTokenRequest.validRefreshToken()) } } diff --git a/apis/src/main/kotlin/org/yapp/apis/auth/service/RefreshTokenService.kt b/apis/src/main/kotlin/org/yapp/apis/auth/service/RefreshTokenService.kt index fee4ef7b..e9ea032a 100644 --- a/apis/src/main/kotlin/org/yapp/apis/auth/service/RefreshTokenService.kt +++ b/apis/src/main/kotlin/org/yapp/apis/auth/service/RefreshTokenService.kt @@ -7,20 +7,20 @@ import org.yapp.apis.auth.dto.request.TokenGenerateRequest import org.yapp.apis.auth.dto.request.TokenRefreshRequest import org.yapp.apis.auth.dto.response.RefreshTokenResponse import org.yapp.apis.auth.dto.response.UserIdResponse -import org.yapp.domain.token.TokenDomainRedisService +import org.yapp.domain.token.RefreshTokenDomainService import java.util.* @Service @Validated class RefreshTokenService( - private val tokenDomainRedisService: TokenDomainRedisService, + private val refreshTokenDomainService: RefreshTokenDomainService, ) { fun deleteRefreshTokenByToken(token: String) { - tokenDomainRedisService.deleteRefreshTokenByToken(token) + refreshTokenDomainService.deleteRefreshTokenByToken(token) } fun saveRefreshToken(@Valid tokenGenerateRequest: TokenGenerateRequest): RefreshTokenResponse { - val token = tokenDomainRedisService.saveRefreshToken( + val token = refreshTokenDomainService.saveRefreshToken( tokenGenerateRequest.validUserId(), tokenGenerateRequest.validRefreshToken(), tokenGenerateRequest.validExpiration() @@ -29,16 +29,16 @@ class RefreshTokenService( } fun getRefreshTokenByUserId(userId: UUID): RefreshTokenResponse { - val token = tokenDomainRedisService.getRefreshTokenByUserId(userId) + val token = refreshTokenDomainService.getRefreshTokenByUserId(userId) return RefreshTokenResponse.from(token) } fun validateRefreshToken(refreshToken: String) { - tokenDomainRedisService.validateRefreshTokenByToken(refreshToken) + refreshTokenDomainService.validateRefreshTokenByToken(refreshToken) } fun getUserIdByToken(@Valid tokenRefreshRequest: TokenRefreshRequest): UserIdResponse { - val userId = tokenDomainRedisService.getUserIdByToken(tokenRefreshRequest.validRefreshToken()) + val userId = refreshTokenDomainService.getUserIdByToken(tokenRefreshRequest.validRefreshToken()) return UserIdResponse.from(userId) } } diff --git a/apis/src/main/kotlin/org/yapp/apis/auth/service/UserSignInService.kt b/apis/src/main/kotlin/org/yapp/apis/auth/service/UserSignInService.kt new file mode 100644 index 00000000..12589608 --- /dev/null +++ b/apis/src/main/kotlin/org/yapp/apis/auth/service/UserSignInService.kt @@ -0,0 +1,27 @@ +package org.yapp.apis.auth.service + +import jakarta.validation.Valid +import org.springframework.stereotype.Service +import org.springframework.transaction.annotation.Transactional +import org.yapp.apis.user.dto.request.FindOrCreateUserRequest +import org.yapp.apis.user.dto.response.CreateUserResponse +import org.yapp.apis.user.service.UserAccountService + +@Service +class UserSignInService( + private val userAccountService: UserAccountService, +) { + @Transactional + fun processSignIn( + @Valid request: FindOrCreateUserRequest, + appleRefreshToken: String? + ): CreateUserResponse { + val createUserResponse = userAccountService.findOrCreateUser(request) + + appleRefreshToken?.let { + userAccountService.updateAppleRefreshToken(createUserResponse.id, it) + } + + return createUserResponse + } +} diff --git a/apis/src/main/kotlin/org/yapp/apis/auth/service/UserWithdrawalService.kt b/apis/src/main/kotlin/org/yapp/apis/auth/service/UserWithdrawalService.kt new file mode 100644 index 00000000..a397be0f --- /dev/null +++ b/apis/src/main/kotlin/org/yapp/apis/auth/service/UserWithdrawalService.kt @@ -0,0 +1,20 @@ +package org.yapp.apis.auth.service + +import org.springframework.stereotype.Service +import org.springframework.transaction.annotation.Transactional +import org.yapp.apis.user.service.UserAccountService +import java.util.* + +@Service +class UserWithdrawalService( + private val userAccountService: UserAccountService, + private val refreshTokenService: RefreshTokenService +) { + @Transactional + fun processWithdrawal(userId: UUID) { + userAccountService.withdrawUser(userId) + + val refreshTokenResponse = refreshTokenService.getRefreshTokenByUserId(userId) + refreshTokenService.deleteRefreshTokenByToken(refreshTokenResponse.refreshToken) + } +} diff --git a/apis/src/main/kotlin/org/yapp/apis/auth/strategy/AuthStrategy.kt b/apis/src/main/kotlin/org/yapp/apis/auth/strategy/AuthStrategy.kt deleted file mode 100644 index 6bf7e028..00000000 --- a/apis/src/main/kotlin/org/yapp/apis/auth/strategy/AuthStrategy.kt +++ /dev/null @@ -1,15 +0,0 @@ -package org.yapp.apis.auth.strategy - -import org.yapp.apis.auth.dto.response.UserCreateInfoResponse -import org.yapp.domain.user.ProviderType - -/** - * Strategy interface for authentication. - * This interface defines the contract for different authentication strategies. - */ -interface AuthStrategy { - - fun getProviderType(): ProviderType - - fun authenticate(credentials: AuthCredentials): UserCreateInfoResponse -} diff --git a/apis/src/main/kotlin/org/yapp/apis/auth/strategy/AppleAuthStrategy.kt b/apis/src/main/kotlin/org/yapp/apis/auth/strategy/signin/AppleSignInStrategy.kt similarity index 84% rename from apis/src/main/kotlin/org/yapp/apis/auth/strategy/AppleAuthStrategy.kt rename to apis/src/main/kotlin/org/yapp/apis/auth/strategy/signin/AppleSignInStrategy.kt index b094550f..4f892792 100644 --- a/apis/src/main/kotlin/org/yapp/apis/auth/strategy/AppleAuthStrategy.kt +++ b/apis/src/main/kotlin/org/yapp/apis/auth/strategy/signin/AppleSignInStrategy.kt @@ -1,4 +1,4 @@ -package org.yapp.apis.auth.strategy +package org.yapp.apis.auth.strategy.signin import mu.KotlinLogging import org.springframework.stereotype.Component @@ -10,24 +10,21 @@ import org.yapp.apis.auth.util.NicknameGenerator import org.yapp.domain.user.ProviderType @Component -class AppleAuthStrategy( +class AppleSignInStrategy( private val appleIdTokenProcessor: AppleIdTokenProcessor -) : AuthStrategy { +) : SignInStrategy { private val log = KotlinLogging.logger {} override fun getProviderType(): ProviderType = ProviderType.APPLE - override fun authenticate(credentials: AuthCredentials): UserCreateInfoResponse { + override fun authenticate(credentials: SignInCredentials): UserCreateInfoResponse { val appleCredentials = validateCredentials(credentials) - val payload = appleIdTokenProcessor.parseAndValidate(appleCredentials.idToken) - log.info { "Apple ID Token validated successfully. sub=${payload.sub}, email=${payload.email}" } - return createUserInfo(payload) } - private fun validateCredentials(credentials: AuthCredentials): AppleAuthCredentials { + private fun validateCredentials(credentials: SignInCredentials): AppleAuthCredentials { return credentials as? AppleAuthCredentials ?: throw AuthException( AuthErrorCode.INVALID_CREDENTIALS, diff --git a/apis/src/main/kotlin/org/yapp/apis/auth/strategy/KakaoAuthStrategy.kt b/apis/src/main/kotlin/org/yapp/apis/auth/strategy/signin/KakaoSignInStrategy.kt similarity index 86% rename from apis/src/main/kotlin/org/yapp/apis/auth/strategy/KakaoAuthStrategy.kt rename to apis/src/main/kotlin/org/yapp/apis/auth/strategy/signin/KakaoSignInStrategy.kt index 3d82ef93..ba417179 100644 --- a/apis/src/main/kotlin/org/yapp/apis/auth/strategy/KakaoAuthStrategy.kt +++ b/apis/src/main/kotlin/org/yapp/apis/auth/strategy/signin/KakaoSignInStrategy.kt @@ -1,4 +1,4 @@ -package org.yapp.apis.auth.strategy +package org.yapp.apis.auth.strategy.signin import mu.KotlinLogging import org.springframework.stereotype.Component @@ -11,15 +11,15 @@ import org.yapp.domain.user.ProviderType import org.yapp.infra.external.oauth.kakao.response.KakaoUserInfo @Component -class KakaoAuthStrategy( +class KakaoSignInStrategy( private val kakaoApiManager: KakaoApiManager -) : AuthStrategy { +) : SignInStrategy { private val log = KotlinLogging.logger {} override fun getProviderType(): ProviderType = ProviderType.KAKAO - override fun authenticate(credentials: AuthCredentials): UserCreateInfoResponse { + override fun authenticate(credentials: SignInCredentials): UserCreateInfoResponse { return try { val kakaoCredentials = validateCredentials(credentials) val kakaoUser = kakaoApiManager.getUserInfo(kakaoCredentials.accessToken) @@ -33,7 +33,7 @@ class KakaoAuthStrategy( } } - private fun validateCredentials(credentials: AuthCredentials): KakaoAuthCredentials { + private fun validateCredentials(credentials: SignInCredentials): KakaoAuthCredentials { return credentials as? KakaoAuthCredentials ?: throw AuthException( AuthErrorCode.INVALID_CREDENTIALS, diff --git a/apis/src/main/kotlin/org/yapp/apis/auth/strategy/AuthCredentials.kt b/apis/src/main/kotlin/org/yapp/apis/auth/strategy/signin/SignInCredentials.kt similarity index 75% rename from apis/src/main/kotlin/org/yapp/apis/auth/strategy/AuthCredentials.kt rename to apis/src/main/kotlin/org/yapp/apis/auth/strategy/signin/SignInCredentials.kt index 8ff9fc55..a274481c 100644 --- a/apis/src/main/kotlin/org/yapp/apis/auth/strategy/AuthCredentials.kt +++ b/apis/src/main/kotlin/org/yapp/apis/auth/strategy/signin/SignInCredentials.kt @@ -1,20 +1,20 @@ -package org.yapp.apis.auth.strategy +package org.yapp.apis.auth.strategy.signin import org.yapp.domain.user.ProviderType -sealed class AuthCredentials { +sealed class SignInCredentials { abstract fun getProviderType(): ProviderType } data class KakaoAuthCredentials( val accessToken: String -) : AuthCredentials() { +) : SignInCredentials() { override fun getProviderType(): ProviderType = ProviderType.KAKAO } data class AppleAuthCredentials( val idToken: String, val authorizationCode: String -) : AuthCredentials() { +) : SignInCredentials() { override fun getProviderType(): ProviderType = ProviderType.APPLE } diff --git a/apis/src/main/kotlin/org/yapp/apis/auth/strategy/signin/SignInStrategy.kt b/apis/src/main/kotlin/org/yapp/apis/auth/strategy/signin/SignInStrategy.kt new file mode 100644 index 00000000..97ac6e7f --- /dev/null +++ b/apis/src/main/kotlin/org/yapp/apis/auth/strategy/signin/SignInStrategy.kt @@ -0,0 +1,11 @@ +package org.yapp.apis.auth.strategy.signin + +import org.yapp.apis.auth.dto.response.UserCreateInfoResponse +import org.yapp.domain.user.ProviderType + +interface SignInStrategy { + + fun getProviderType(): ProviderType + + fun authenticate(credentials: SignInCredentials): UserCreateInfoResponse +} diff --git a/apis/src/main/kotlin/org/yapp/apis/auth/strategy/AuthStrategyResolver.kt b/apis/src/main/kotlin/org/yapp/apis/auth/strategy/signin/SignInStrategyResolver.kt similarity index 70% rename from apis/src/main/kotlin/org/yapp/apis/auth/strategy/AuthStrategyResolver.kt rename to apis/src/main/kotlin/org/yapp/apis/auth/strategy/signin/SignInStrategyResolver.kt index 97f08b62..a9a32a90 100644 --- a/apis/src/main/kotlin/org/yapp/apis/auth/strategy/AuthStrategyResolver.kt +++ b/apis/src/main/kotlin/org/yapp/apis/auth/strategy/signin/SignInStrategyResolver.kt @@ -1,14 +1,14 @@ -package org.yapp.apis.auth.strategy +package org.yapp.apis.auth.strategy.signin import org.springframework.stereotype.Service import org.yapp.apis.auth.exception.AuthErrorCode import org.yapp.apis.auth.exception.AuthException @Service -class AuthStrategyResolver( - private val strategies: List +class SignInStrategyResolver( + private val strategies: List ) { - fun resolve(credentials: AuthCredentials): AuthStrategy { + fun resolve(credentials: SignInCredentials): SignInStrategy { return strategies.find { it.getProviderType() == credentials.getProviderType() } ?: throw AuthException( AuthErrorCode.UNSUPPORTED_PROVIDER_TYPE, diff --git a/apis/src/main/kotlin/org/yapp/apis/auth/strategy/withdraw/AppleWithdrawStrategy.kt b/apis/src/main/kotlin/org/yapp/apis/auth/strategy/withdraw/AppleWithdrawStrategy.kt new file mode 100644 index 00000000..b124daf5 --- /dev/null +++ b/apis/src/main/kotlin/org/yapp/apis/auth/strategy/withdraw/AppleWithdrawStrategy.kt @@ -0,0 +1,22 @@ +package org.yapp.apis.auth.strategy.withdraw + +import org.springframework.stereotype.Component +import org.yapp.apis.auth.dto.request.WithdrawStrategyRequest +import org.yapp.apis.auth.exception.AuthErrorCode +import org.yapp.apis.auth.exception.AuthException +import org.yapp.apis.auth.manager.AppleApiManager +import org.yapp.domain.user.ProviderType + +@Component +class AppleWithdrawStrategy( + private val appleApiManager: AppleApiManager +) : WithdrawStrategy { + override fun getProviderType(): ProviderType = ProviderType.APPLE + + override fun withdraw(request: WithdrawStrategyRequest) { + val appleRefreshToken = request.appleRefreshToken + ?: throw AuthException(AuthErrorCode.APPLE_REFRESH_TOKEN_MISSING) + + appleApiManager.revokeToken(appleRefreshToken) + } +} diff --git a/apis/src/main/kotlin/org/yapp/apis/auth/strategy/withdraw/KakaoWithdrawStrategy.kt b/apis/src/main/kotlin/org/yapp/apis/auth/strategy/withdraw/KakaoWithdrawStrategy.kt new file mode 100644 index 00000000..f6b45ca0 --- /dev/null +++ b/apis/src/main/kotlin/org/yapp/apis/auth/strategy/withdraw/KakaoWithdrawStrategy.kt @@ -0,0 +1,17 @@ +package org.yapp.apis.auth.strategy.withdraw + +import org.springframework.stereotype.Component +import org.yapp.apis.auth.dto.request.WithdrawStrategyRequest +import org.yapp.apis.auth.manager.KakaoApiManager +import org.yapp.domain.user.ProviderType + +@Component +class KakaoWithdrawStrategy( + private val kakaoApiManager: KakaoApiManager +) : WithdrawStrategy { + override fun getProviderType() = ProviderType.KAKAO + + override fun withdraw(request: WithdrawStrategyRequest) { +// kakaoApiManager.unlink(request.providerId) + } +} diff --git a/apis/src/main/kotlin/org/yapp/apis/auth/strategy/withdraw/WithdrawStrategy.kt b/apis/src/main/kotlin/org/yapp/apis/auth/strategy/withdraw/WithdrawStrategy.kt new file mode 100644 index 00000000..e0d417e7 --- /dev/null +++ b/apis/src/main/kotlin/org/yapp/apis/auth/strategy/withdraw/WithdrawStrategy.kt @@ -0,0 +1,11 @@ +package org.yapp.apis.auth.strategy.withdraw + +import org.yapp.apis.auth.dto.request.WithdrawStrategyRequest +import org.yapp.domain.user.ProviderType + +interface WithdrawStrategy { + + fun getProviderType(): ProviderType + + fun withdraw(request: WithdrawStrategyRequest) +} diff --git a/apis/src/main/kotlin/org/yapp/apis/auth/strategy/withdraw/WithdrawStrategyResolver.kt b/apis/src/main/kotlin/org/yapp/apis/auth/strategy/withdraw/WithdrawStrategyResolver.kt new file mode 100644 index 00000000..6de0b754 --- /dev/null +++ b/apis/src/main/kotlin/org/yapp/apis/auth/strategy/withdraw/WithdrawStrategyResolver.kt @@ -0,0 +1,14 @@ +package org.yapp.apis.auth.strategy.withdraw + +import org.springframework.stereotype.Component +import org.yapp.domain.user.ProviderType + +@Component +class WithdrawStrategyResolver( + private val strategies: List +) { + fun resolve(providerType: ProviderType): WithdrawStrategy { + return strategies.find { it.getProviderType() == providerType } + ?: throw IllegalArgumentException("Unsupported provider type for withdrawal: $providerType") + } +} diff --git a/apis/src/main/kotlin/org/yapp/apis/auth/usecase/AuthUseCase.kt b/apis/src/main/kotlin/org/yapp/apis/auth/usecase/AuthUseCase.kt index f572731f..822cc1ec 100644 --- a/apis/src/main/kotlin/org/yapp/apis/auth/usecase/AuthUseCase.kt +++ b/apis/src/main/kotlin/org/yapp/apis/auth/usecase/AuthUseCase.kt @@ -3,41 +3,44 @@ package org.yapp.apis.auth.usecase import org.springframework.transaction.annotation.Transactional import org.yapp.apis.auth.dto.request.* import org.yapp.apis.auth.dto.response.TokenPairResponse -import org.yapp.apis.auth.dto.response.UserProfileResponse -import org.yapp.apis.auth.service.AppleAuthService -import org.yapp.apis.auth.service.AuthTokenService -import org.yapp.apis.auth.service.RefreshTokenService -import org.yapp.apis.auth.service.UserAuthService -import org.yapp.apis.auth.strategy.AppleAuthCredentials -import org.yapp.apis.auth.strategy.AuthStrategyResolver +import org.yapp.apis.auth.exception.AuthErrorCode +import org.yapp.apis.auth.exception.AuthException +import org.yapp.apis.auth.service.* +import org.yapp.apis.auth.strategy.signin.AppleAuthCredentials +import org.yapp.apis.auth.strategy.signin.SignInCredentials +import org.yapp.apis.auth.strategy.signin.SignInStrategyResolver +import org.yapp.apis.auth.strategy.withdraw.WithdrawStrategyResolver +import org.yapp.apis.user.dto.request.FindOrCreateUserRequest +import org.yapp.apis.user.dto.request.FindUserIdentityRequest +import org.yapp.apis.user.service.UserAccountService +import org.yapp.apis.user.service.UserService import org.yapp.globalutils.annotation.UseCase import java.util.* @UseCase @Transactional(readOnly = true) class AuthUseCase( - private val authStrategyResolver: AuthStrategyResolver, - private val userAuthService: UserAuthService, + private val signInStrategyResolver: SignInStrategyResolver, + private val withdrawStrategyResolver: WithdrawStrategyResolver, + private val userService: UserService, + private val userAccountService: UserAccountService, + private val userSignInService: UserSignInService, + private val userWithdrawalService: UserWithdrawalService, private val refreshTokenService: RefreshTokenService, private val authTokenService: AuthTokenService, private val appleAuthService: AppleAuthService ) { - @Transactional fun signIn(socialLoginRequest: SocialLoginRequest): TokenPairResponse { val credentials = SocialLoginRequest.toCredentials(socialLoginRequest) - val strategy = authStrategyResolver.resolve(credentials) + val strategy = signInStrategyResolver.resolve(credentials) val userCreateInfoResponse = strategy.authenticate(credentials) - val createUserResponse = userAuthService.findOrCreateUser(FindOrCreateUserRequest.from(userCreateInfoResponse)) + val appleRefreshToken = fetchAppleRefreshTokenIfNeeded(credentials) - if (credentials is AppleAuthCredentials) { - appleAuthService.saveAppleRefreshTokenIfMissing( - SaveAppleRefreshTokenRequest.of( - createUserResponse, - credentials - ) - ) - } + val createUserResponse = userSignInService.processSignIn( + FindOrCreateUserRequest.from(userCreateInfoResponse), + appleRefreshToken + ) return authTokenService.generateTokenPair(GenerateTokenPairRequest.from(createUserResponse)) } @@ -45,10 +48,10 @@ class AuthUseCase( @Transactional fun reissueTokenPair(tokenRefreshRequest: TokenRefreshRequest): TokenPairResponse { val userIdResponse = authTokenService.validateAndGetUserIdFromRefreshToken(tokenRefreshRequest) - authTokenService.deleteTokenForReissue(tokenRefreshRequest) + authTokenService.deleteRefreshTokenForReissue(tokenRefreshRequest) val findUserIdentityRequest = FindUserIdentityRequest.from(userIdResponse) - val userAuthInfoResponse = userAuthService.findUserIdentityByUserId(findUserIdentityRequest) + val userAuthInfoResponse = userService.findUserIdentityByUserId(findUserIdentityRequest) val generateTokenPairRequest = GenerateTokenPairRequest.from(userAuthInfoResponse) return authTokenService.generateTokenPair(generateTokenPairRequest) @@ -57,16 +60,29 @@ class AuthUseCase( @Transactional fun signOut(userId: UUID) { val refreshTokenResponse = refreshTokenService.getRefreshTokenByUserId(userId) - val deleteTokenRequest = DeleteTokenRequest.from(refreshTokenResponse) - authTokenService.deleteTokenForSignOut(deleteTokenRequest) + authTokenService.deleteRefreshTokenForSignOutOrWithdraw(DeleteTokenRequest.from(refreshTokenResponse)) } - fun getUserProfile(userId: UUID): UserProfileResponse { - return userAuthService.findUserProfileByUserId(userId) + fun withdraw(userId: UUID, withdrawRequest: WithdrawRequest) { + val withdrawTargetUserResponse = userAccountService.findWithdrawUserById(userId) + + if (withdrawTargetUserResponse.providerType != withdrawRequest.validProviderType()) { + throw AuthException( + AuthErrorCode.PROVIDER_TYPE_MISMATCH, + "The provider type in the request does not match the user's actual provider type." + ) + } + + val strategy = withdrawStrategyResolver.resolve(withdrawRequest.validProviderType()) + strategy.withdraw(WithdrawStrategyRequest.from(withdrawTargetUserResponse)) + + userWithdrawalService.processWithdrawal(userId) } - @Transactional - fun updateTermsAgreement(userId: UUID, termsAgreed: Boolean): UserProfileResponse { - return userAuthService.updateTermsAgreement(userId, termsAgreed) + private fun fetchAppleRefreshTokenIfNeeded(credentials: SignInCredentials): String? { + if (credentials is AppleAuthCredentials) { + return appleAuthService.fetchAppleOauthTokens(credentials.authorizationCode).refreshToken + } + return null } } diff --git a/apis/src/main/kotlin/org/yapp/apis/auth/dto/request/UserBooksByIsbnsRequest.kt b/apis/src/main/kotlin/org/yapp/apis/book/dto/request/UserBooksByIsbnsRequest.kt similarity index 96% rename from apis/src/main/kotlin/org/yapp/apis/auth/dto/request/UserBooksByIsbnsRequest.kt rename to apis/src/main/kotlin/org/yapp/apis/book/dto/request/UserBooksByIsbnsRequest.kt index 0cde7ed0..fbc0d8c9 100644 --- a/apis/src/main/kotlin/org/yapp/apis/auth/dto/request/UserBooksByIsbnsRequest.kt +++ b/apis/src/main/kotlin/org/yapp/apis/book/dto/request/UserBooksByIsbnsRequest.kt @@ -1,4 +1,4 @@ -package org.yapp.apis.auth.dto.request +package org.yapp.apis.book.dto.request import io.swagger.v3.oas.annotations.media.Schema import jakarta.validation.constraints.NotEmpty @@ -25,8 +25,6 @@ data class UserBooksByIsbnsRequest private constructor( val isbns: List? = null ) { - - fun validUserId(): UUID = userId!! fun validIsbns(): List = isbns!! diff --git a/apis/src/main/kotlin/org/yapp/apis/book/service/UserBookService.kt b/apis/src/main/kotlin/org/yapp/apis/book/service/UserBookService.kt index 4a318616..7b0db48c 100644 --- a/apis/src/main/kotlin/org/yapp/apis/book/service/UserBookService.kt +++ b/apis/src/main/kotlin/org/yapp/apis/book/service/UserBookService.kt @@ -5,7 +5,7 @@ import org.springframework.data.domain.Page import org.springframework.data.domain.Pageable import org.springframework.stereotype.Service import org.springframework.validation.annotation.Validated -import org.yapp.apis.auth.dto.request.UserBooksByIsbnsRequest +import org.yapp.apis.book.dto.request.UserBooksByIsbnsRequest import org.yapp.apis.book.dto.request.UpsertUserBookRequest import org.yapp.apis.book.dto.response.UserBookPageResponse import org.yapp.apis.book.dto.response.UserBookResponse diff --git a/apis/src/main/kotlin/org/yapp/apis/book/usecase/BookUseCase.kt b/apis/src/main/kotlin/org/yapp/apis/book/usecase/BookUseCase.kt index 7569f2bc..25e5e5c5 100644 --- a/apis/src/main/kotlin/org/yapp/apis/book/usecase/BookUseCase.kt +++ b/apis/src/main/kotlin/org/yapp/apis/book/usecase/BookUseCase.kt @@ -4,9 +4,7 @@ package org.yapp.apis.book.usecase import org.springframework.beans.factory.annotation.Qualifier import org.springframework.data.domain.Pageable import org.springframework.transaction.annotation.Transactional -import org.yapp.apis.auth.dto.request.UserBooksByIsbnsRequest -import org.yapp.apis.auth.service.UserAuthService - +import org.yapp.apis.book.dto.request.UserBooksByIsbnsRequest import org.yapp.apis.book.dto.request.* import org.yapp.apis.book.dto.response.BookDetailResponse import org.yapp.apis.book.dto.response.BookSearchResponse @@ -15,6 +13,7 @@ import org.yapp.apis.book.dto.response.UserBookResponse import org.yapp.apis.book.service.BookManagementService import org.yapp.apis.book.service.BookQueryService import org.yapp.apis.book.service.UserBookService +import org.yapp.apis.user.service.UserService import org.yapp.domain.userbook.BookStatus import org.yapp.domain.userbook.UserBookSortType import org.yapp.globalutils.annotation.UseCase @@ -25,7 +24,7 @@ import java.util.* class BookUseCase( @Qualifier("aladinBookQueryService") private val bookQueryService: BookQueryService, - private val userAuthService: UserAuthService, + private val userService: UserService, private val userBookService: UserBookService, private val bookManagementService: BookManagementService ) { @@ -33,7 +32,7 @@ class BookUseCase( request: BookSearchRequest, userId: UUID ): BookSearchResponse { - userAuthService.validateUserExists(userId) + userService.validateUserExists(userId) val searchResponse = bookQueryService.searchBooks(request) val booksWithUserStatus = mergeWithUserBookStatus(searchResponse.books, userId) @@ -45,13 +44,13 @@ class BookUseCase( bookDetailRequest: BookDetailRequest, userId: UUID ): BookDetailResponse { - userAuthService.validateUserExists(userId) + userService.validateUserExists(userId) val bookDetailResponse = bookQueryService.getBookDetail(bookDetailRequest) val isbn13 = bookDetailResponse.isbn13 ?: return bookDetailResponse.withUserBookStatus(BookStatus.BEFORE_REGISTRATION) - - val userBookStatus = userBookService.findUserBookStatusByIsbn(userId, isbn13) + + val userBookStatus = userBookService.findUserBookStatusByIsbn(userId, isbn13) ?: BookStatus.BEFORE_REGISTRATION return bookDetailResponse.withUserBookStatus(userBookStatus) @@ -62,7 +61,7 @@ class BookUseCase( userId: UUID, request: UserBookRegisterRequest ): UserBookResponse { - userAuthService.validateUserExists(userId) + userService.validateUserExists(userId) val bookDetailResponse = bookQueryService.getBookDetail(BookDetailRequest.from(request.validBookIsbn())) val bookCreateResponse = bookManagementService.findOrCreateBook(BookCreateRequest.from(bookDetailResponse)) @@ -83,7 +82,7 @@ class BookUseCase( title: String?, pageable: Pageable ): UserBookPageResponse { - userAuthService.validateUserExists(userId) + userService.validateUserExists(userId) return userBookService.findUserBooksByDynamicConditionWithStatusCounts(userId, status, sort, title, pageable) } diff --git a/apis/src/main/kotlin/org/yapp/apis/readingrecord/usecase/ReadingRecordUseCase.kt b/apis/src/main/kotlin/org/yapp/apis/readingrecord/usecase/ReadingRecordUseCase.kt index 477cfafa..376a5223 100644 --- a/apis/src/main/kotlin/org/yapp/apis/readingrecord/usecase/ReadingRecordUseCase.kt +++ b/apis/src/main/kotlin/org/yapp/apis/readingrecord/usecase/ReadingRecordUseCase.kt @@ -3,11 +3,11 @@ package org.yapp.apis.readingrecord.usecase import org.springframework.data.domain.Page import org.springframework.data.domain.Pageable import org.springframework.transaction.annotation.Transactional -import org.yapp.apis.auth.service.UserAuthService import org.yapp.apis.book.service.UserBookService import org.yapp.apis.readingrecord.dto.request.CreateReadingRecordRequest import org.yapp.apis.readingrecord.dto.response.ReadingRecordResponse import org.yapp.apis.readingrecord.service.ReadingRecordService +import org.yapp.apis.user.service.UserService import org.yapp.domain.readingrecord.ReadingRecordSortType import org.yapp.globalutils.annotation.UseCase import java.util.* @@ -16,7 +16,7 @@ import java.util.* @Transactional(readOnly = true) class ReadingRecordUseCase( private val readingRecordService: ReadingRecordService, - private val userAuthService: UserAuthService, + private val userService: UserService, private val userBookService: UserBookService, ) { @Transactional @@ -25,7 +25,7 @@ class ReadingRecordUseCase( userBookId: UUID, request: CreateReadingRecordRequest ): ReadingRecordResponse { - userAuthService.validateUserExists(userId) + userService.validateUserExists(userId) userBookService.validateUserBookExists(userBookId, userId) return readingRecordService.createReadingRecord( @@ -39,7 +39,7 @@ class ReadingRecordUseCase( userId: UUID, readingRecordId: UUID ): ReadingRecordResponse { - userAuthService.validateUserExists(userId) + userService.validateUserExists(userId) return readingRecordService.getReadingRecordDetail( userId = userId, @@ -53,7 +53,7 @@ class ReadingRecordUseCase( sort: ReadingRecordSortType?, pageable: Pageable ): Page { - userAuthService.validateUserExists(userId) + userService.validateUserExists(userId) userBookService.validateUserBookExists(userBookId, userId) return readingRecordService.getReadingRecordsByDynamicCondition( diff --git a/apis/src/main/kotlin/org/yapp/apis/seed/usecase/SeedUseCase.kt b/apis/src/main/kotlin/org/yapp/apis/seed/usecase/SeedUseCase.kt index fb78ae60..0abd7920 100644 --- a/apis/src/main/kotlin/org/yapp/apis/seed/usecase/SeedUseCase.kt +++ b/apis/src/main/kotlin/org/yapp/apis/seed/usecase/SeedUseCase.kt @@ -1,20 +1,20 @@ package org.yapp.apis.seed.usecase import org.springframework.transaction.annotation.Transactional -import org.yapp.apis.auth.service.UserAuthService import org.yapp.apis.seed.dto.response.SeedStatsResponse import org.yapp.apis.seed.service.SeedService +import org.yapp.apis.user.service.UserService import org.yapp.globalutils.annotation.UseCase import java.util.* @UseCase @Transactional(readOnly = true) class SeedUseCase( - private val userAuthService: UserAuthService, + private val userService: UserService, private val seedService: SeedService ) { fun getSeedStats(userId: UUID): SeedStatsResponse { - userAuthService.validateUserExists(userId) + userService.validateUserExists(userId) return seedService.getSeedStatsByUserId(userId) } } diff --git a/apis/src/main/kotlin/org/yapp/apis/user/controller/UserController.kt b/apis/src/main/kotlin/org/yapp/apis/user/controller/UserController.kt new file mode 100644 index 00000000..e20fc536 --- /dev/null +++ b/apis/src/main/kotlin/org/yapp/apis/user/controller/UserController.kt @@ -0,0 +1,33 @@ +package org.yapp.apis.user.controller + +import jakarta.validation.Valid +import org.springframework.http.ResponseEntity +import org.springframework.security.core.annotation.AuthenticationPrincipal +import org.springframework.web.bind.annotation.* +import org.yapp.apis.user.dto.request.TermsAgreementRequest +import org.yapp.apis.user.dto.response.UserProfileResponse +import org.yapp.apis.user.usecase.UserUseCase +import java.util.* + +@RestController +@RequestMapping("/api/v1/users") +class UserController( + private val userUseCase: UserUseCase +) : UserControllerApi { + @GetMapping("/me") + override fun getUserProfile( + @AuthenticationPrincipal userId: UUID + ): ResponseEntity { + val userProfile = userUseCase.getUserProfile(userId) + return ResponseEntity.ok(userProfile) + } + + @PutMapping("/me/terms-agreement") + override fun updateTermsAgreement( + @AuthenticationPrincipal userId: UUID, + @Valid @RequestBody request: TermsAgreementRequest + ): ResponseEntity { + val userProfile = userUseCase.updateTermsAgreement(userId, request) + return ResponseEntity.ok(userProfile) + } +} diff --git a/apis/src/main/kotlin/org/yapp/apis/user/controller/UserControllerApi.kt b/apis/src/main/kotlin/org/yapp/apis/user/controller/UserControllerApi.kt new file mode 100644 index 00000000..870b8909 --- /dev/null +++ b/apis/src/main/kotlin/org/yapp/apis/user/controller/UserControllerApi.kt @@ -0,0 +1,87 @@ +package org.yapp.apis.user.controller + +import io.swagger.v3.oas.annotations.Operation +import io.swagger.v3.oas.annotations.Parameter +import io.swagger.v3.oas.annotations.media.Content +import io.swagger.v3.oas.annotations.media.Schema +import io.swagger.v3.oas.annotations.responses.ApiResponse +import io.swagger.v3.oas.annotations.responses.ApiResponses +import io.swagger.v3.oas.annotations.tags.Tag +import jakarta.validation.Valid +import org.springframework.http.ResponseEntity +import org.springframework.security.core.annotation.AuthenticationPrincipal +import org.springframework.web.bind.annotation.GetMapping +import org.springframework.web.bind.annotation.PutMapping +import org.springframework.web.bind.annotation.RequestBody +import org.springframework.web.bind.annotation.RequestMapping +import org.yapp.apis.user.dto.request.TermsAgreementRequest +import org.yapp.apis.user.dto.response.UserProfileResponse +import org.yapp.globalutils.exception.ErrorResponse +import java.util.* + +@Tag(name = "Users", description = "사용자 정보를 관리하는 API") +@RequestMapping("/api/v1/users") +interface UserControllerApi { + + @Operation( + summary = "사용자 프로필 조회", + description = "현재 인증된 사용자의 프로필 정보를 조회합니다." + ) + @ApiResponses( + value = [ + ApiResponse( + responseCode = "200", + description = "사용자 프로필 조회 성공", + content = [Content(schema = Schema(implementation = UserProfileResponse::class))] + ), + ApiResponse( + responseCode = "401", + description = "인증되지 않은 사용자", + content = [Content(schema = Schema(implementation = ErrorResponse::class))] + ), + ApiResponse( + responseCode = "404", + description = "사용자를 찾을 수 없습니다.", + content = [Content(schema = Schema(implementation = ErrorResponse::class))] + ) + ] + ) + @GetMapping("/me") + fun getUserProfile( + @Parameter(hidden = true) @AuthenticationPrincipal userId: UUID + ): ResponseEntity + + @Operation( + summary = "약관 동의 상태 업데이트", + description = "사용자의 약관 동의 상태를 업데이트합니다." + ) + @ApiResponses( + value = [ + ApiResponse( + responseCode = "200", + description = "약관 동의 상태 업데이트 성공", + content = [Content(schema = Schema(implementation = UserProfileResponse::class))] + ), + ApiResponse( + responseCode = "400", + description = "잘못된 요청 파라미터", + content = [Content(schema = Schema(implementation = ErrorResponse::class))] + ), + ApiResponse( + responseCode = "401", + description = "인증되지 않은 사용자", + content = [Content(schema = Schema(implementation = ErrorResponse::class))] + ), + ApiResponse( + responseCode = "404", + description = "사용자를 찾을 수 없습니다.", + content = [Content(schema = Schema(implementation = ErrorResponse::class))] + ) + ] + ) + @PutMapping("/me/terms-agreement") + fun updateTermsAgreement( + @Parameter(hidden = true) @AuthenticationPrincipal userId: UUID, + @Valid @RequestBody @Parameter(description = "약관 동의 요청 객체") request: TermsAgreementRequest + ): ResponseEntity +} diff --git a/apis/src/main/kotlin/org/yapp/apis/auth/dto/request/FindOrCreateUserRequest.kt b/apis/src/main/kotlin/org/yapp/apis/user/dto/request/FindOrCreateUserRequest.kt similarity index 98% rename from apis/src/main/kotlin/org/yapp/apis/auth/dto/request/FindOrCreateUserRequest.kt rename to apis/src/main/kotlin/org/yapp/apis/user/dto/request/FindOrCreateUserRequest.kt index 9642ee30..8ce0a947 100644 --- a/apis/src/main/kotlin/org/yapp/apis/auth/dto/request/FindOrCreateUserRequest.kt +++ b/apis/src/main/kotlin/org/yapp/apis/user/dto/request/FindOrCreateUserRequest.kt @@ -1,4 +1,4 @@ -package org.yapp.apis.auth.dto.request +package org.yapp.apis.user.dto.request import io.swagger.v3.oas.annotations.media.Schema import jakarta.validation.constraints.NotBlank diff --git a/apis/src/main/kotlin/org/yapp/apis/auth/dto/request/FindUserIdentityRequest.kt b/apis/src/main/kotlin/org/yapp/apis/user/dto/request/FindUserIdentityRequest.kt similarity index 95% rename from apis/src/main/kotlin/org/yapp/apis/auth/dto/request/FindUserIdentityRequest.kt rename to apis/src/main/kotlin/org/yapp/apis/user/dto/request/FindUserIdentityRequest.kt index ffff9255..4971a03a 100644 --- a/apis/src/main/kotlin/org/yapp/apis/auth/dto/request/FindUserIdentityRequest.kt +++ b/apis/src/main/kotlin/org/yapp/apis/user/dto/request/FindUserIdentityRequest.kt @@ -1,4 +1,4 @@ -package org.yapp.apis.auth.dto.request +package org.yapp.apis.user.dto.request import io.swagger.v3.oas.annotations.media.Schema import jakarta.validation.constraints.NotNull diff --git a/apis/src/main/kotlin/org/yapp/apis/auth/dto/request/TermsAgreementRequest.kt b/apis/src/main/kotlin/org/yapp/apis/user/dto/request/TermsAgreementRequest.kt similarity index 92% rename from apis/src/main/kotlin/org/yapp/apis/auth/dto/request/TermsAgreementRequest.kt rename to apis/src/main/kotlin/org/yapp/apis/user/dto/request/TermsAgreementRequest.kt index cd8197f2..4edfea40 100644 --- a/apis/src/main/kotlin/org/yapp/apis/auth/dto/request/TermsAgreementRequest.kt +++ b/apis/src/main/kotlin/org/yapp/apis/user/dto/request/TermsAgreementRequest.kt @@ -1,4 +1,4 @@ -package org.yapp.apis.auth.dto.request +package org.yapp.apis.user.dto.request import io.swagger.v3.oas.annotations.media.Schema import jakarta.validation.constraints.NotNull diff --git a/apis/src/main/kotlin/org/yapp/apis/auth/dto/response/CreateUserResponse.kt b/apis/src/main/kotlin/org/yapp/apis/user/dto/response/CreateUserResponse.kt similarity index 96% rename from apis/src/main/kotlin/org/yapp/apis/auth/dto/response/CreateUserResponse.kt rename to apis/src/main/kotlin/org/yapp/apis/user/dto/response/CreateUserResponse.kt index 687f177e..6749c2f8 100644 --- a/apis/src/main/kotlin/org/yapp/apis/auth/dto/response/CreateUserResponse.kt +++ b/apis/src/main/kotlin/org/yapp/apis/user/dto/response/CreateUserResponse.kt @@ -1,4 +1,4 @@ -package org.yapp.apis.auth.dto.response +package org.yapp.apis.user.dto.response import io.swagger.v3.oas.annotations.media.Schema import org.yapp.domain.user.vo.UserAuthVO diff --git a/apis/src/main/kotlin/org/yapp/apis/auth/dto/response/UserAuthInfoResponse.kt b/apis/src/main/kotlin/org/yapp/apis/user/dto/response/UserAuthInfoResponse.kt similarity index 95% rename from apis/src/main/kotlin/org/yapp/apis/auth/dto/response/UserAuthInfoResponse.kt rename to apis/src/main/kotlin/org/yapp/apis/user/dto/response/UserAuthInfoResponse.kt index 6bdefaf1..03d5c542 100644 --- a/apis/src/main/kotlin/org/yapp/apis/auth/dto/response/UserAuthInfoResponse.kt +++ b/apis/src/main/kotlin/org/yapp/apis/user/dto/response/UserAuthInfoResponse.kt @@ -1,4 +1,4 @@ -package org.yapp.apis.auth.dto.response +package org.yapp.apis.user.dto.response import io.swagger.v3.oas.annotations.media.Schema import org.yapp.domain.user.vo.UserIdentityVO diff --git a/apis/src/main/kotlin/org/yapp/apis/auth/dto/response/UserProfileResponse.kt b/apis/src/main/kotlin/org/yapp/apis/user/dto/response/UserProfileResponse.kt similarity index 97% rename from apis/src/main/kotlin/org/yapp/apis/auth/dto/response/UserProfileResponse.kt rename to apis/src/main/kotlin/org/yapp/apis/user/dto/response/UserProfileResponse.kt index 0a9461c7..d840712b 100644 --- a/apis/src/main/kotlin/org/yapp/apis/auth/dto/response/UserProfileResponse.kt +++ b/apis/src/main/kotlin/org/yapp/apis/user/dto/response/UserProfileResponse.kt @@ -1,4 +1,4 @@ -package org.yapp.apis.auth.dto.response +package org.yapp.apis.user.dto.response import io.swagger.v3.oas.annotations.media.Schema import org.yapp.domain.user.ProviderType diff --git a/apis/src/main/kotlin/org/yapp/apis/user/dto/response/WithdrawTargetUserResponse.kt b/apis/src/main/kotlin/org/yapp/apis/user/dto/response/WithdrawTargetUserResponse.kt new file mode 100644 index 00000000..3f313a38 --- /dev/null +++ b/apis/src/main/kotlin/org/yapp/apis/user/dto/response/WithdrawTargetUserResponse.kt @@ -0,0 +1,36 @@ +package org.yapp.apis.user.dto.response + +import io.swagger.v3.oas.annotations.media.Schema +import org.yapp.domain.user.ProviderType +import org.yapp.domain.user.vo.WithdrawTargetUserVO +import java.util.UUID + +@Schema( + name = "WithdrawUserResponse", + description = "회원 탈퇴에 필요한 사용자 정보 응답 DTO" +) +data class WithdrawTargetUserResponse private constructor( + + @Schema(description = "사용자 ID") + val id: UUID, + + @Schema(description = "소셜 로그인 제공사 타입") + val providerType: ProviderType, + + @Schema(description = "소셜 제공사로부터 발급받은 고유 ID") + val providerId: String, + + @Schema(description = "Apple Refresh Token (애플 회원 탈퇴 시 필요, 카카오는 null)") + val appleRefreshToken: String? = null +) { + companion object { + fun from(vo: WithdrawTargetUserVO): WithdrawTargetUserResponse { + return WithdrawTargetUserResponse( + id = vo.id.value, + providerType = vo.providerType, + providerId = vo.providerId.value, + appleRefreshToken = vo.appleRefreshToken + ) + } + } +} diff --git a/apis/src/main/kotlin/org/yapp/apis/auth/service/UserAuthService.kt b/apis/src/main/kotlin/org/yapp/apis/user/service/UserAccountService.kt similarity index 60% rename from apis/src/main/kotlin/org/yapp/apis/auth/service/UserAuthService.kt rename to apis/src/main/kotlin/org/yapp/apis/user/service/UserAccountService.kt index 190ecf9f..7a22a942 100644 --- a/apis/src/main/kotlin/org/yapp/apis/auth/service/UserAuthService.kt +++ b/apis/src/main/kotlin/org/yapp/apis/user/service/UserAccountService.kt @@ -1,46 +1,22 @@ -package org.yapp.apis.auth.service +package org.yapp.apis.user.service import jakarta.validation.Valid import org.springframework.stereotype.Service import org.springframework.validation.annotation.Validated -import org.yapp.apis.auth.dto.request.FindOrCreateUserRequest -import org.yapp.apis.auth.dto.request.FindUserIdentityRequest -import org.yapp.apis.auth.dto.response.CreateUserResponse -import org.yapp.apis.auth.dto.response.UserAuthInfoResponse -import org.yapp.apis.auth.dto.response.UserProfileResponse import org.yapp.apis.auth.exception.AuthErrorCode import org.yapp.apis.auth.exception.AuthException +import org.yapp.apis.user.dto.request.FindOrCreateUserRequest +import org.yapp.apis.user.dto.response.CreateUserResponse +import org.yapp.apis.user.dto.response.WithdrawTargetUserResponse import org.yapp.domain.user.UserDomainService import org.yapp.domain.user.vo.UserAuthVO import java.util.* @Service @Validated -class UserAuthService( +class UserAccountService( private val userDomainService: UserDomainService ) { - fun findUserProfileByUserId(userId: UUID): UserProfileResponse { - val userProfile = userDomainService.findUserProfileById(userId) - return UserProfileResponse.from(userProfile) - } - - fun updateTermsAgreement(userId: UUID, termsAgreed: Boolean): UserProfileResponse { - validateUserExists(userId) - val updatedUserProfile = userDomainService.updateTermsAgreement(userId, termsAgreed) - return UserProfileResponse.from(updatedUserProfile) - } - - fun validateUserExists(userId: UUID) { - if (!userDomainService.existsActiveUserByIdAndDeletedAtIsNull(userId)) { - throw AuthException(AuthErrorCode.USER_NOT_FOUND, "User not found: $userId") - } - } - - fun findUserIdentityByUserId(@Valid findUserIdentityRequest: FindUserIdentityRequest): UserAuthInfoResponse { - val userIdentity = userDomainService.findUserIdentityById(findUserIdentityRequest.validUserId()) - return UserAuthInfoResponse.from(userIdentity) - } - fun findOrCreateUser(@Valid findOrCreateUserRequest: FindOrCreateUserRequest): CreateUserResponse { userDomainService.findUserByProviderTypeAndProviderId( findOrCreateUserRequest.validProviderType(), @@ -61,6 +37,19 @@ class UserAuthService( return CreateUserResponse.from(createdUser) } + fun findWithdrawUserById(userId: UUID): WithdrawTargetUserResponse { + val withdrawTargetUserVO = userDomainService.findWithdrawUserById(userId) + return WithdrawTargetUserResponse.from(withdrawTargetUserVO) + } + + fun withdrawUser(userId: UUID) { + userDomainService.deleteUser(userId) + } + + fun updateAppleRefreshToken(userId: UUID, refreshToken: String) { + userDomainService.updateAppleRefreshToken(userId, refreshToken) + } + private fun createNewUser(@Valid findOrCreateUserRequest: FindOrCreateUserRequest): UserAuthVO { val email = findOrCreateUserRequest.getOrDefaultEmail() val nickname = findOrCreateUserRequest.getOrDefaultNickname() diff --git a/apis/src/main/kotlin/org/yapp/apis/user/service/UserService.kt b/apis/src/main/kotlin/org/yapp/apis/user/service/UserService.kt new file mode 100644 index 00000000..eae1ca53 --- /dev/null +++ b/apis/src/main/kotlin/org/yapp/apis/user/service/UserService.kt @@ -0,0 +1,41 @@ +package org.yapp.apis.user.service + +import jakarta.validation.Valid +import org.springframework.stereotype.Service +import org.springframework.validation.annotation.Validated +import org.yapp.apis.user.dto.request.FindUserIdentityRequest +import org.yapp.apis.user.dto.response.UserAuthInfoResponse +import org.yapp.apis.auth.exception.AuthErrorCode +import org.yapp.apis.auth.exception.AuthException +import org.yapp.apis.user.dto.request.TermsAgreementRequest +import org.yapp.apis.user.dto.response.UserProfileResponse +import org.yapp.domain.user.UserDomainService +import java.util.* + +@Service +@Validated +class UserService( + private val userDomainService: UserDomainService +) { + fun findUserProfileByUserId(userId: UUID): UserProfileResponse { + val userProfile = userDomainService.findUserProfileById(userId) + return UserProfileResponse.from(userProfile) + } + + fun updateTermsAgreement(userId: UUID, @Valid request: TermsAgreementRequest): UserProfileResponse { + validateUserExists(userId) + val updatedUserProfile = userDomainService.updateTermsAgreement(userId, request.validTermsAgreed()) + return UserProfileResponse.from(updatedUserProfile) + } + + fun validateUserExists(userId: UUID) { + if (!userDomainService.existsActiveUserByIdAndDeletedAtIsNull(userId)) { + throw AuthException(AuthErrorCode.USER_NOT_FOUND, "User not found: $userId") + } + } + + fun findUserIdentityByUserId(@Valid findUserIdentityRequest: FindUserIdentityRequest): UserAuthInfoResponse { + val userIdentity = userDomainService.findUserIdentityById(findUserIdentityRequest.validUserId()) + return UserAuthInfoResponse.from(userIdentity) + } +} diff --git a/apis/src/main/kotlin/org/yapp/apis/user/usecase/UserUseCase.kt b/apis/src/main/kotlin/org/yapp/apis/user/usecase/UserUseCase.kt new file mode 100644 index 00000000..412823eb --- /dev/null +++ b/apis/src/main/kotlin/org/yapp/apis/user/usecase/UserUseCase.kt @@ -0,0 +1,23 @@ +package org.yapp.apis.user.usecase + +import org.springframework.transaction.annotation.Transactional +import org.yapp.apis.user.dto.request.TermsAgreementRequest +import org.yapp.apis.user.dto.response.UserProfileResponse +import org.yapp.apis.user.service.UserService +import org.yapp.globalutils.annotation.UseCase +import java.util.* + +@UseCase +@Transactional(readOnly = true) +class UserUseCase( + private val userService: UserService +) { + fun getUserProfile(userId: UUID): UserProfileResponse { + return userService.findUserProfileByUserId(userId) + } + + @Transactional + fun updateTermsAgreement(userId: UUID, request: TermsAgreementRequest): UserProfileResponse { + return userService.updateTermsAgreement(userId, request) + } +} diff --git a/domain/src/main/kotlin/org/yapp/domain/token/TokenDomainRedisService.kt b/domain/src/main/kotlin/org/yapp/domain/token/RefreshTokenDomainService.kt similarity index 98% rename from domain/src/main/kotlin/org/yapp/domain/token/TokenDomainRedisService.kt rename to domain/src/main/kotlin/org/yapp/domain/token/RefreshTokenDomainService.kt index 723d9e27..75b4bd35 100644 --- a/domain/src/main/kotlin/org/yapp/domain/token/TokenDomainRedisService.kt +++ b/domain/src/main/kotlin/org/yapp/domain/token/RefreshTokenDomainService.kt @@ -9,7 +9,7 @@ import java.time.LocalDateTime import java.util.* @DomainService -class TokenDomainRedisService( +class RefreshTokenDomainService( private val refreshTokenRepository: RefreshTokenRepository ) { fun saveRefreshToken(userId: UUID, refreshToken: String, expiration: Long): Token { diff --git a/domain/src/main/kotlin/org/yapp/domain/user/UserDomainService.kt b/domain/src/main/kotlin/org/yapp/domain/user/UserDomainService.kt index e0fe64f3..6ddc6d65 100644 --- a/domain/src/main/kotlin/org/yapp/domain/user/UserDomainService.kt +++ b/domain/src/main/kotlin/org/yapp/domain/user/UserDomainService.kt @@ -5,6 +5,7 @@ import org.yapp.domain.user.exception.UserNotFoundException import org.yapp.domain.user.vo.UserAuthVO import org.yapp.domain.user.vo.UserIdentityVO import org.yapp.domain.user.vo.UserProfileVO +import org.yapp.domain.user.vo.WithdrawTargetUserVO import org.yapp.globalutils.annotation.DomainService import java.util.* @@ -23,6 +24,11 @@ class UserDomainService( return UserIdentityVO.newInstance(user) } + fun findWithdrawUserById(id: UUID): WithdrawTargetUserVO { + val user = userRepository.findById(id) ?: throw UserNotFoundException(UserErrorCode.USER_NOT_FOUND) + return WithdrawTargetUserVO.newInstance(user) + } + fun findUserByProviderTypeAndProviderId(providerType: ProviderType, providerId: String): UserAuthVO? { return userRepository.findByProviderTypeAndProviderId(providerType, providerId) ?.let { UserAuthVO.newInstance(it) } @@ -86,4 +92,11 @@ class UserDomainService( return UserIdentityVO.newInstance(updatedUser) } + + fun deleteUser(userId: UUID) { + val user = userRepository.findById(userId) + ?: throw UserNotFoundException(UserErrorCode.USER_NOT_FOUND) + + userRepository.deleteById(user.id.value) + } } diff --git a/domain/src/main/kotlin/org/yapp/domain/user/UserRepository.kt b/domain/src/main/kotlin/org/yapp/domain/user/UserRepository.kt index 114dc130..517693ed 100644 --- a/domain/src/main/kotlin/org/yapp/domain/user/UserRepository.kt +++ b/domain/src/main/kotlin/org/yapp/domain/user/UserRepository.kt @@ -2,9 +2,6 @@ package org.yapp.domain.user import java.util.* -/** - * Repository interface for User domain model. - */ interface UserRepository { fun findById(id: UUID): User? @@ -18,4 +15,6 @@ interface UserRepository { fun existsById(id: UUID): Boolean fun existsByEmail(email: String): Boolean + + fun deleteById(userId: UUID): Unit } diff --git a/domain/src/main/kotlin/org/yapp/domain/user/vo/WithdrawTargetUserVO.kt b/domain/src/main/kotlin/org/yapp/domain/user/vo/WithdrawTargetUserVO.kt new file mode 100644 index 00000000..d14a6532 --- /dev/null +++ b/domain/src/main/kotlin/org/yapp/domain/user/vo/WithdrawTargetUserVO.kt @@ -0,0 +1,22 @@ +package org.yapp.domain.user.vo + +import org.yapp.domain.user.ProviderType +import org.yapp.domain.user.User + +data class WithdrawTargetUserVO private constructor( + val id: User.Id, + val providerType: ProviderType, + val providerId: User.ProviderId, + val appleRefreshToken: String? +) { + companion object { + fun newInstance(user: User): WithdrawTargetUserVO { + return WithdrawTargetUserVO( + id = user.id, + providerType = user.providerType, + providerId = user.providerId, + appleRefreshToken = user.appleRefreshToken + ) + } + } +} diff --git a/infra/src/main/kotlin/org/yapp/infra/external/oauth/apple/AppleApi.kt b/infra/src/main/kotlin/org/yapp/infra/external/oauth/apple/AppleApi.kt index 7a8dfc40..c7cfed00 100644 --- a/infra/src/main/kotlin/org/yapp/infra/external/oauth/apple/AppleApi.kt +++ b/infra/src/main/kotlin/org/yapp/infra/external/oauth/apple/AppleApi.kt @@ -36,7 +36,7 @@ class AppleApi( } } - fun revokeToken( + fun revokeAppleToken( clientId: String, clientSecret: String, token: String, diff --git a/infra/src/main/kotlin/org/yapp/infra/external/redis/entity/RefreshToken.kt b/infra/src/main/kotlin/org/yapp/infra/external/redis/entity/RefreshTokenEntity.kt similarity index 100% rename from infra/src/main/kotlin/org/yapp/infra/external/redis/entity/RefreshToken.kt rename to infra/src/main/kotlin/org/yapp/infra/external/redis/entity/RefreshTokenEntity.kt diff --git a/infra/src/main/kotlin/org/yapp/infra/user/repository/impl/UserRepositoryImpl.kt b/infra/src/main/kotlin/org/yapp/infra/user/repository/impl/UserRepositoryImpl.kt index d7edf391..f081b2cd 100644 --- a/infra/src/main/kotlin/org/yapp/infra/user/repository/impl/UserRepositoryImpl.kt +++ b/infra/src/main/kotlin/org/yapp/infra/user/repository/impl/UserRepositoryImpl.kt @@ -41,4 +41,8 @@ class UserRepositoryImpl( ): User? { return jpaUserRepository.findByProviderTypeAndProviderIdIncludingDeleted(providerType, providerId)?.toDomain() } + + override fun deleteById(userId: UUID) { + return jpaUserRepository.deleteById(userId) + } }