비밀번호 찾기 API 추가 #4
| @@ -0,0 +1,30 @@ | ||||
| package kr.co.vividnext.sodalive.configs | ||||
|  | ||||
| import com.amazonaws.auth.AWSStaticCredentialsProvider | ||||
| import com.amazonaws.auth.BasicAWSCredentials | ||||
| import com.amazonaws.services.simpleemail.AmazonSimpleEmailService | ||||
| import com.amazonaws.services.simpleemail.AmazonSimpleEmailServiceClientBuilder | ||||
| import org.springframework.beans.factory.annotation.Value | ||||
| import org.springframework.context.annotation.Bean | ||||
| import org.springframework.context.annotation.Configuration | ||||
|  | ||||
| @Configuration | ||||
| class AmazonSESConfig( | ||||
|     @Value("\${cloud.aws.credentials.access-key}") | ||||
|     private val accessKey: String, | ||||
|     @Value("\${cloud.aws.credentials.secret-key}") | ||||
|     private val secretKey: String, | ||||
|     @Value("\${cloud.aws.region.static}") | ||||
|     private val region: String | ||||
| ) { | ||||
|     @Bean | ||||
|     fun amazonSimpleEmailService(): AmazonSimpleEmailService { | ||||
|         val basicAWSCredentials = BasicAWSCredentials(accessKey, secretKey) | ||||
|         val awsStaticCredentialsProvider = AWSStaticCredentialsProvider(basicAWSCredentials) | ||||
|  | ||||
|         return AmazonSimpleEmailServiceClientBuilder.standard() | ||||
|             .withCredentials(awsStaticCredentialsProvider) | ||||
|             .withRegion(region) | ||||
|             .build() | ||||
|     } | ||||
| } | ||||
| @@ -0,0 +1,18 @@ | ||||
| package kr.co.vividnext.sodalive.email | ||||
|  | ||||
| import com.amazonaws.services.simpleemail.AmazonSimpleEmailService | ||||
| import org.springframework.stereotype.Service | ||||
|  | ||||
| @Service | ||||
| class SendEmailService(private val amazonSimpleEmailService: AmazonSimpleEmailService) { | ||||
|     fun sendTemplatedEmail(template: String, templateData: String, receiver: String) { | ||||
|         val senderDto = TemplatedEmailSenderDto( | ||||
|             senderEmail = "yozmlive.noreply@gmail.com", | ||||
|             template = template, | ||||
|             templateData = templateData, | ||||
|             to = receiver | ||||
|         ) | ||||
|  | ||||
|         amazonSimpleEmailService.sendTemplatedEmail(senderDto.toSendRequest()) | ||||
|     } | ||||
| } | ||||
| @@ -0,0 +1,21 @@ | ||||
| package kr.co.vividnext.sodalive.email | ||||
|  | ||||
| import com.amazonaws.services.simpleemail.model.Destination | ||||
| import com.amazonaws.services.simpleemail.model.SendTemplatedEmailRequest | ||||
|  | ||||
| data class TemplatedEmailSenderDto( | ||||
|     private val senderEmail: String, | ||||
|     private val template: String, | ||||
|     private val templateData: String, | ||||
|     private val to: String | ||||
| ) { | ||||
|     fun toSendRequest(): SendTemplatedEmailRequest { | ||||
|         val destination = Destination().withToAddresses(to) | ||||
|  | ||||
|         return SendTemplatedEmailRequest() | ||||
|             .withTemplate(template) | ||||
|             .withDestination(destination) | ||||
|             .withSource(senderEmail) | ||||
|             .withTemplateData(templateData) | ||||
|     } | ||||
| } | ||||
| @@ -0,0 +1,3 @@ | ||||
| package kr.co.vividnext.sodalive.member | ||||
|  | ||||
| data class ForgotPasswordRequest(val email: String) | ||||
| @@ -193,4 +193,9 @@ class MemberController(private val service: MemberService) { | ||||
|         @RequestParam("image") multipartFile: MultipartFile, | ||||
|         @AuthenticationPrincipal user: User | ||||
|     ) = ApiResponse.ok(service.profileImageUpdate(multipartFile, user)) | ||||
|  | ||||
|     @PostMapping("/forgot-password") | ||||
|     fun forgotPassword( | ||||
|         @RequestBody request: ForgotPasswordRequest | ||||
|     ) = ApiResponse.ok(service.forgotPassword(request)) | ||||
| } | ||||
|   | ||||
| @@ -39,6 +39,7 @@ interface MemberQueryRepository { | ||||
|     fun getMessageRecipientPushToken(messageId: Long): GetMessageRecipientPushTokenResponse | ||||
|     fun getIndividualRecipientPushTokens(recipients: List<Long>, isAuth: Boolean): Map<String, List<List<String>>> | ||||
|     fun getChangeNicknamePrice(memberId: Long): GetChangeNicknamePriceResponse | ||||
|     fun getMemberByEmail(email: String): Member? | ||||
| } | ||||
|  | ||||
| @Repository | ||||
| @@ -229,4 +230,11 @@ class MemberQueryRepositoryImpl( | ||||
|             } | ||||
|         ) | ||||
|     } | ||||
|  | ||||
|     override fun getMemberByEmail(email: String): Member? { | ||||
|         return queryFactory | ||||
|             .selectFrom(member) | ||||
|             .where(member.email.eq(email)) | ||||
|             .fetchOne() | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -7,6 +7,7 @@ import kr.co.vividnext.sodalive.can.payment.CanPaymentService | ||||
| import kr.co.vividnext.sodalive.can.use.CanUsage | ||||
| import kr.co.vividnext.sodalive.common.ApiResponse | ||||
| import kr.co.vividnext.sodalive.common.SodaException | ||||
| import kr.co.vividnext.sodalive.email.SendEmailService | ||||
| import kr.co.vividnext.sodalive.jwt.TokenProvider | ||||
| import kr.co.vividnext.sodalive.live.room.detail.GetRoomDetailUser | ||||
| import kr.co.vividnext.sodalive.member.block.BlockMember | ||||
| @@ -33,6 +34,7 @@ import kr.co.vividnext.sodalive.member.tag.MemberCreatorTag | ||||
| import kr.co.vividnext.sodalive.member.tag.MemberTagRepository | ||||
| import kr.co.vividnext.sodalive.member.token.MemberTokenRepository | ||||
| import kr.co.vividnext.sodalive.utils.generateFileName | ||||
| import kr.co.vividnext.sodalive.utils.generatePassword | ||||
| import org.springframework.beans.factory.annotation.Value | ||||
| import org.springframework.data.repository.findByIdOrNull | ||||
| import org.springframework.security.authentication.UsernamePasswordAuthenticationToken | ||||
| @@ -46,6 +48,9 @@ import org.springframework.security.crypto.password.PasswordEncoder | ||||
| import org.springframework.stereotype.Service | ||||
| import org.springframework.transaction.annotation.Transactional | ||||
| import org.springframework.web.multipart.MultipartFile | ||||
| import java.time.LocalDateTime | ||||
| import java.time.ZoneId | ||||
| import java.time.format.DateTimeFormatter | ||||
| import java.util.concurrent.locks.ReentrantReadWriteLock | ||||
| import kotlin.concurrent.write | ||||
|  | ||||
| @@ -62,6 +67,7 @@ class MemberService( | ||||
|     private val nicknameChangeLogRepository: NicknameChangeLogRepository, | ||||
|     private val memberTagRepository: MemberTagRepository, | ||||
|  | ||||
|     private val emailService: SendEmailService, | ||||
|     private val canPaymentService: CanPaymentService, | ||||
|     private val memberNotificationService: MemberNotificationService, | ||||
|  | ||||
| @@ -546,6 +552,32 @@ class MemberService( | ||||
|         return "$cloudFrontHost/${member.profileImage!!}" | ||||
|     } | ||||
|  | ||||
|     @Transactional | ||||
|     fun forgotPassword(request: ForgotPasswordRequest) { | ||||
|         val member = repository.getMemberByEmail(email = request.email) | ||||
|             ?: throw SodaException("등록되지 않은 계정입니다.\n확인 후 다시 시도해 주세요.") | ||||
|  | ||||
|         val password = generatePassword(12) | ||||
|         member.password = passwordEncoder.encode(password) | ||||
|  | ||||
|         val date = LocalDateTime.now() | ||||
|             .atZone(ZoneId.of("UTC")) | ||||
|             .withZoneSameInstant(ZoneId.of("Asia/Seoul")) | ||||
|             .format(DateTimeFormatter.ofPattern("yyyy년 MM월 dd일, HH:mm")) | ||||
|  | ||||
|         val data = HashMap<String, String>() | ||||
|         data["password"] = password | ||||
|         data["date"] = date | ||||
|  | ||||
|         val templateData = objectMapper.writeValueAsString(data) | ||||
|  | ||||
|         emailService.sendTemplatedEmail( | ||||
|             template = "sodalive-find-password", | ||||
|             templateData = templateData, | ||||
|             receiver = request.email | ||||
|         ) | ||||
|     } | ||||
|  | ||||
|     private fun getOrCreateLock(memberId: Long): ReentrantReadWriteLock { | ||||
|         return tokenLocks.computeIfAbsent(memberId) { ReentrantReadWriteLock() } | ||||
|     } | ||||
|   | ||||
		Reference in New Issue
	
	Block a user