package roomescape.user.business import com.github.f4b6a3.tsid.TsidFactory import io.github.oshai.kotlinlogging.KLogger import io.github.oshai.kotlinlogging.KotlinLogging import org.springframework.data.repository.findByIdOrNull import org.springframework.stereotype.Service import org.springframework.transaction.annotation.Transactional import roomescape.common.config.next import roomescape.common.dto.CurrentUserContext import roomescape.common.dto.UserLoginCredentials import roomescape.common.dto.toCredentials import roomescape.user.exception.UserErrorCode import roomescape.user.exception.UserException import roomescape.user.infrastructure.persistence.* import roomescape.user.web.UserContactResponse import roomescape.user.web.UserCreateRequest import roomescape.user.web.UserCreateResponse import roomescape.user.web.toEntity private val log: KLogger = KotlinLogging.logger {} const val SIGNUP: String = "최초 가입" @Service class UserService( private val userRepository: UserRepository, private val userStatusHistoryRepository: UserStatusHistoryRepository, private val userValidator: UserValidator, private val tsidFactory: TsidFactory ) { @Transactional(readOnly = true) fun findContextById(id: Long): CurrentUserContext { log.info { "[UserService.findContextById] 현재 로그인된 회원 조회 시작: id=${id}" } val user: UserEntity = findOrThrow(id) return CurrentUserContext(user.id, user.name) .also { log.info { "[UserService.findContextById] 현재 로그인된 회원 조회 완료: id=${id}" } } } @Transactional(readOnly = true) fun findCredentialsByAccount(email: String): UserLoginCredentials { log.info { "[UserService.findCredentialsByAccount] 회원 조회 시작: email=${email}" } return userRepository.findByEmail(email) ?.let { log.info { "[UserService.findCredentialsByAccount] 회원 조회 완료: id=${it.id}" } it.toCredentials() } ?: run { log.info { "[UserService.findCredentialsByAccount] 회원 조회 실패" } throw UserException(UserErrorCode.USER_NOT_FOUND) } } @Transactional(readOnly = true) fun findContactById(id: Long) : UserContactResponse { log.info { "[UserService.findContactById] 회원 연락 정보 조회 시작: id=${id}" } val user = findOrThrow(id) return UserContactResponse(user.id, user.name, user.phone) .also { log.info { "[UserService.findContactById] 회원 연락 정보 조회 완료: id=${id}, name=${it.name}" } } } @Transactional fun signup(request: UserCreateRequest): UserCreateResponse { log.info { "[UserService.signup] 회원가입 시작: request:$request" } userValidator.validateCanSignup(request.email, request.phone) val user: UserEntity = userRepository.save( request.toEntity(id = tsidFactory.next(), status = UserStatus.ACTIVE) ).also { log.info { "[UserService.signup] 회원 저장 완료: id:${it.id}" } }.also { createHistory(user = it, reason = SIGNUP) } return UserCreateResponse(user.id, user.name) .also { log.info { "[UserService.signup] 회원가입 완료: id:${it.id}" } } } private fun findOrThrow(id: Long): UserEntity { return userRepository.findByIdOrNull(id) ?: throw UserException(UserErrorCode.USER_NOT_FOUND) } private fun createHistory(user: UserEntity, reason: String): UserStatusHistoryEntity { return userStatusHistoryRepository.save( UserStatusHistoryEntity(id = tsidFactory.next(), userId = user.id, reason = reason, status = user.status) ).also { log.info { "[UserService.signup] 회원 상태 이력 저장 완료: userStatusHistoryId:${it.id}" } } } }