From ef7840502ec1ce15b3981bfb79d051aeb08b445a Mon Sep 17 00:00:00 2001 From: van1164 Date: Thu, 6 Jun 2024 22:55:38 +0900 Subject: [PATCH] =?UTF-8?q?feat:=20=EA=B5=AC=EB=8F=85=20=EA=B8=B0=EB=8A=A5?= =?UTF-8?q?=20=ED=8A=B8=EB=9E=9C=EC=9E=AD=EC=85=98=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- build.gradle.kts | 4 +- .../com/van1164/user/config/UserConfig.kt | 8 ++ .../subscribe/service/SubscribeService.kt | 31 +++++-- .../com/van1164/common/config/R2dbcConfig.kt | 29 ++++++- .../kotlin/service/SubscribeServiceTest.kt | 80 ++++++++++++++++--- 5 files changed, 130 insertions(+), 22 deletions(-) diff --git a/build.gradle.kts b/build.gradle.kts index 71fee99..e114a43 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -58,8 +58,10 @@ subprojects { annotationProcessor("org.projectlombok:lombok") testImplementation("io.projectreactor:reactor-test") + implementation("org.springframework.boot:spring-boot-starter-data-r2dbc") - implementation("com.github.jasync-sql:jasync-r2dbc-mysql:2.2.0") + implementation("io.asyncer:r2dbc-mysql:1.1.0") + implementation("net.bramp.ffmpeg:ffmpeg:0.8.0") implementation("io.github.microutils:kotlin-logging:1.12.0") diff --git a/user/src/main/kotlin/com/van1164/user/config/UserConfig.kt b/user/src/main/kotlin/com/van1164/user/config/UserConfig.kt index e208e85..bc906c7 100644 --- a/user/src/main/kotlin/com/van1164/user/config/UserConfig.kt +++ b/user/src/main/kotlin/com/van1164/user/config/UserConfig.kt @@ -1,4 +1,12 @@ package com.van1164.user.config +import com.van1164.user.UserRepository +import com.van1164.user.subscribe.repository.SubscribeRepository +import org.springframework.context.annotation.Configuration +import org.springframework.data.r2dbc.repository.config.EnableR2dbcRepositories + + +@Configuration +@EnableR2dbcRepositories(basePackageClasses = [SubscribeRepository::class,UserRepository::class]) class UserConfig { } \ No newline at end of file diff --git a/user/src/main/kotlin/com/van1164/user/subscribe/service/SubscribeService.kt b/user/src/main/kotlin/com/van1164/user/subscribe/service/SubscribeService.kt index 65615cc..8fb9e14 100644 --- a/user/src/main/kotlin/com/van1164/user/subscribe/service/SubscribeService.kt +++ b/user/src/main/kotlin/com/van1164/user/subscribe/service/SubscribeService.kt @@ -2,20 +2,28 @@ package com.van1164.user.subscribe.service import com.van1164.common.domain.UserSubscribe import com.van1164.common.exception.AlreadySubscribeException +import com.van1164.common.redis.RedisService import com.van1164.user.UserRepository import com.van1164.user.subscribe.repository.SubscribeRepository import org.springframework.data.crossstore.ChangeSetPersister.NotFoundException +import org.springframework.data.r2dbc.repository.config.EnableR2dbcRepositories import org.springframework.http.ResponseEntity import org.springframework.stereotype.Service +import org.springframework.transaction.TransactionManager import org.springframework.transaction.annotation.Transactional +import org.springframework.transaction.reactive.TransactionalOperator import reactor.core.publisher.Mono import reactor.kotlin.core.publisher.switchIfEmpty import reactor.kotlin.core.publisher.toMono +import java.lang.Exception @Service +@EnableR2dbcRepositories(basePackageClasses = [SubscribeRepository::class,UserRepository::class]) class SubscribeService( private val subscribeRepository: SubscribeRepository, - private val userRepository: UserRepository + private val userRepository: UserRepository, + private val redisService: RedisService, + private val transactionalOperator: TransactionalOperator, ) { @Transactional @@ -34,17 +42,19 @@ class SubscribeService( .map{ UserSubscribe(fromUserId,toUserId) } + .flatMap(subscribeRepository::save) .flatMap { - subscribeRepository.save(it) + redisService.increaseSubscribe(it.toUserId) } .map { ResponseEntity.ok().body("success") } + .`as`(transactionalOperator::transactional) .onErrorResume { when(it){ is NotFoundException -> ResponseEntity.badRequest().body("존재하지 않는 User").toMono() is AlreadySubscribeException -> ResponseEntity.badRequest().body("이미 구독 중").toMono() - else -> ResponseEntity.badRequest().body("fail").toMono() + else -> ResponseEntity.internalServerError().body("fail").toMono() } } } @@ -52,14 +62,19 @@ class SubscribeService( @Transactional fun subscribeCancel(fromUserId: String, toUserId: String): Mono> { return subscribeRepository.findByFromUserIdAndToUserId(fromUserId,toUserId) - .doOnNext { - println(it) - } + .switchIfEmpty { Mono.error(NotFoundException()) } .flatMap { subscribeRepository.delete(it) } + .then(Mono.defer { redisService.decreaseSubscribe(toUserId) }) .thenReturn(ResponseEntity.ok().body("success")) - .defaultIfEmpty(ResponseEntity.badRequest().body("존재하지 않는 User")) - .onErrorReturn(ResponseEntity.badRequest().body("fail")) + .switchIfEmpty(Mono.error(Exception())) + .onErrorResume { + when(it){ + is NotFoundException -> ResponseEntity.badRequest().body("구독중이 아닙니다.").toMono() + else -> ResponseEntity.internalServerError().body("fail").toMono() + } + } + } } \ No newline at end of file diff --git a/util/src/main/kotlin/com/van1164/common/config/R2dbcConfig.kt b/util/src/main/kotlin/com/van1164/common/config/R2dbcConfig.kt index 6ab2ff5..3f49315 100644 --- a/util/src/main/kotlin/com/van1164/common/config/R2dbcConfig.kt +++ b/util/src/main/kotlin/com/van1164/common/config/R2dbcConfig.kt @@ -1,4 +1,31 @@ package com.van1164.common.config -class R2dbcConfig { +import io.r2dbc.spi.ConnectionFactory +import org.springframework.context.annotation.Bean +import org.springframework.context.annotation.Configuration +import org.springframework.data.r2dbc.repository.config.EnableR2dbcRepositories +import org.springframework.r2dbc.connection.R2dbcTransactionManager +import org.springframework.transaction.ReactiveTransactionManager +import org.springframework.transaction.annotation.EnableTransactionManagement +import org.springframework.transaction.annotation.TransactionManagementConfigurer +import org.springframework.transaction.reactive.TransactionalOperator + + +@Configuration +@EnableR2dbcRepositories +@EnableTransactionManagement +class R2dbcConfig(private val connectionFactory: ConnectionFactory) : TransactionManagementConfigurer { + @Bean + fun transactionManager(): ReactiveTransactionManager { + return R2dbcTransactionManager(connectionFactory) + } + + @Bean + fun transactionalOperator(transactionManager: ReactiveTransactionManager): TransactionalOperator { + return TransactionalOperator.create(transactionManager) + } + + override fun annotationDrivenTransactionManager(): ReactiveTransactionManager { + return transactionManager() + } } \ No newline at end of file diff --git a/video/src/test/kotlin/service/SubscribeServiceTest.kt b/video/src/test/kotlin/service/SubscribeServiceTest.kt index d371a07..1aa5e26 100644 --- a/video/src/test/kotlin/service/SubscribeServiceTest.kt +++ b/video/src/test/kotlin/service/SubscribeServiceTest.kt @@ -1,14 +1,20 @@ package service +import com.van1164.common.redis.RedisRepository import com.van1164.common.test.Transaction import com.van1164.user.subscribe.service.SubscribeService +import com.van1164.util.SUBSCRIBE_PREFIX import com.van1164.video.VideoApplication +import org.junit.jupiter.api.AfterEach +import org.junit.jupiter.api.BeforeAll +import org.junit.jupiter.api.BeforeEach import org.junit.jupiter.api.DisplayName import org.junit.jupiter.api.Test import org.springframework.beans.factory.annotation.Autowired import org.springframework.boot.test.context.SpringBootTest import org.springframework.http.HttpStatus import org.springframework.test.annotation.Rollback +import reactor.core.publisher.Mono import reactor.test.StepVerifier import kotlin.test.assertEquals import kotlin.test.assertNotNull @@ -17,14 +23,27 @@ import kotlin.test.assertNotNull @SpringBootTest(classes = [VideoApplication::class]) class SubscribeServiceTest @Autowired constructor( val subscribeService: SubscribeService, - val transaction: Transaction + val transaction: Transaction, + val redisRepository: RedisRepository, ) { + val testEmail = "GOOGLE_van1154van@gmail.com" + + @BeforeEach + fun beforeEach(){ + redisRepository.remove(SUBSCRIBE_PREFIX + testEmail).block() + } + + @AfterEach + fun afterEach(){ + redisRepository.remove(SUBSCRIBE_PREFIX + testEmail).block() + } + @Test @DisplayName("구독 성공 테스트") @Rollback(value = true) fun successTest(){ - subscribeService.subscribe("GOOGLE_van1154van@gmail.com","GOOGLE_van1154van@gmail.com") + subscribeService.subscribe(testEmail,testEmail) .`as`(transaction::withRollback) .`as`(StepVerifier::create) .assertNext{response-> @@ -33,6 +52,15 @@ class SubscribeServiceTest @Autowired constructor( assertNotNull(response.body) assertEquals(response.body,"success") } + .verifyComplete() + + redisRepository.load(SUBSCRIBE_PREFIX+testEmail) + .defaultIfEmpty("X") + .`as`(StepVerifier::create) + .assertNext{ + assertEquals(it,"1") + } + .verifyComplete() } @@ -40,7 +68,7 @@ class SubscribeServiceTest @Autowired constructor( @DisplayName("없는 아이디로 인한 구독 실패 테스트") @Rollback(value = true) fun noUserIdFailTest(){ - subscribeService.subscribe("GOOGLE_van1154van@gmail.com","test@gmail.com") + subscribeService.subscribe("test@gmail.com",testEmail) .`as`(transaction::withRollback) .`as`(StepVerifier::create) .assertNext{response-> @@ -49,14 +77,25 @@ class SubscribeServiceTest @Autowired constructor( assertNotNull(response.body) assertEquals(response.body ,"존재하지 않는 User") } + .verifyComplete() + + redisRepository.load(SUBSCRIBE_PREFIX+testEmail) + .defaultIfEmpty("X") + .`as`(StepVerifier::create) + .assertNext{ + assertEquals(it,"X") + } + .verifyComplete() } @Test @DisplayName("이미 구독한 사용자 실패 테스트") @Rollback(value = true) fun duplicatedSubscribeFailTest(){ - subscribeService.subscribe("GOOGLE_van1154van@gmail.com","GOOGLE_van1154van@gmail.com").`as`(transaction::withRollback).block() - subscribeService.subscribe("GOOGLE_van1154van@gmail.com","GOOGLE_van1154van@gmail.com") + subscribeService.subscribe(testEmail,testEmail) + .flatMap { + subscribeService.subscribe(testEmail,testEmail) + } .`as`(transaction::withRollback) .`as`(StepVerifier::create) .assertNext{response-> @@ -65,24 +104,31 @@ class SubscribeServiceTest @Autowired constructor( assertNotNull(response.body) assertEquals(response.body ,"이미 구독 중") } + .verifyComplete() + + redisRepository.load(SUBSCRIBE_PREFIX+testEmail) + .defaultIfEmpty("X") + .`as`(StepVerifier::create) + .assertNext{ + assertEquals(it,"1") + } + .verifyComplete() } @Test @DisplayName("구독 취소 성공 테스트") @Rollback(value = true) fun subscribeCancel(){ - subscribeService.subscribe("GOOGLE_van1154van@gmail.com","GOOGLE_van1154van@gmail.com") - .`as`(transaction::withRollback) - .`as`(StepVerifier::create) - .assertNext { response -> + subscribeService.subscribe(testEmail,testEmail) + .doOnNext{ response -> assertNotNull(response) assertEquals(response.statusCode, HttpStatus.OK) assertNotNull(response.body) assertEquals(response.body, "success") } - - - subscribeService.subscribeCancel("GOOGLE_van1154van@gmail.com","GOOGLE_van1154van@gmail.com") + .flatMap { + subscribeService.subscribeCancel(testEmail,testEmail) + } .`as`(transaction::withRollback) .`as`(StepVerifier::create) .assertNext{cancel-> @@ -91,6 +137,16 @@ class SubscribeServiceTest @Autowired constructor( assertNotNull(cancel.body) assertEquals(cancel.body ,"success") } + .verifyComplete() + + + redisRepository.load(SUBSCRIBE_PREFIX+testEmail) + .`as`(StepVerifier::create) + .assertNext{ + assertEquals(it,"0") + } + .verifyComplete() } + } \ No newline at end of file