diff --git a/build.gradle.kts b/build.gradle.kts index f19b209..d7bc6b2 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -37,7 +37,8 @@ dependencies { testImplementation("org.springframework.restdocs:spring-restdocs-webtestclient:3.0.0") testImplementation("com.ninja-squad:springmockk:4.0.2") testImplementation("com.epages:restdocs-api-spec-openapi3-generator:0.17.1") -} + implementation("org.springframework.boot:spring-boot-starter-data-redis-reactive") + implementation("io.projectreactor.kotlin:reactor-kotlin-extensions")} kotlin { compilerOptions { diff --git a/src/main/kotlin/com/yedongsoon/account_service/application/couple/CoupleCommandService.kt b/src/main/kotlin/com/yedongsoon/account_service/application/couple/CoupleCommandService.kt index bb20061..33d6a0d 100644 --- a/src/main/kotlin/com/yedongsoon/account_service/application/couple/CoupleCommandService.kt +++ b/src/main/kotlin/com/yedongsoon/account_service/application/couple/CoupleCommandService.kt @@ -2,18 +2,27 @@ package com.yedongsoon.account_service.application.couple import com.yedongsoon.account_service.application.exception.CoupleExistException import com.yedongsoon.account_service.application.exception.CoupleNotFoundException +import com.yedongsoon.account_service.application.exception.InvalidInviteCodeException import com.yedongsoon.account_service.domain.couple.Couple import com.yedongsoon.account_service.domain.couple.CoupleRepository +import com.yedongsoon.account_service.domain.extension.encodeDtoToBase64 import com.yedongsoon.account_service.domain.member.MemberRepository import com.yedongsoon.account_service.domain.member.model.CoupleCreateCommand import com.yedongsoon.account_service.domain.member.model.CoupleInfoCreateCommand import com.yedongsoon.account_service.domain.member.model.CoupleInfoModifyCommand import org.springframework.data.crossstore.ChangeSetPersister +import org.springframework.data.redis.core.ReactiveRedisTemplate +import org.springframework.data.redis.core.getAndAwait +import org.springframework.data.redis.core.setAndAwait import org.springframework.stereotype.Service +import java.time.Duration +import java.time.LocalDate +import java.time.LocalDateTime @Service class CoupleCommandService( private val coupleRepository: CoupleRepository, + private val redisTemplate: ReactiveRedisTemplate ) { fun createCoupleInfo(command:CoupleInfoCreateCommand) { coupleRepository.findByAccountNoAOrAccountNoB(command.memberNo, command.memberNo)?.let { @@ -22,8 +31,9 @@ class CoupleCommandService( } ?: throw CoupleNotFoundException("커플 정보가 존재하지 않습니다") } - fun createCouple(command: CoupleCreateCommand) { - coupleRepository.findByAccountNoAOrAccountNoB(command.accountNoA, command.accountNoB)?.let { + suspend fun createCouple(command: CoupleCreateCommand) { + val memberNo = redisTemplate.opsForValue().getAndAwait(command.inviteCode) ?: throw InvalidInviteCodeException("유효하지 않은 초대코드입니다.") + coupleRepository.findByAccountNoAOrAccountNoB(memberNo.toInt(), command.accountNoB)?.let { throw CoupleExistException("이미 커플이 존재하는 회원입니다.") } coupleRepository.save(Couple.create(command)) @@ -37,4 +47,9 @@ class CoupleCommandService( } + + suspend fun createInviteCode(memberNo: Int) { + val code=memberNo.toString()+LocalDateTime.now() + redisTemplate.opsForValue().setAndAwait(code.encodeDtoToBase64(),memberNo.toString(), Duration.ofMinutes(5)) + } } \ No newline at end of file diff --git a/src/main/kotlin/com/yedongsoon/account_service/application/exception/CoupleException.kt b/src/main/kotlin/com/yedongsoon/account_service/application/exception/CoupleException.kt index ba54be2..1101e02 100644 --- a/src/main/kotlin/com/yedongsoon/account_service/application/exception/CoupleException.kt +++ b/src/main/kotlin/com/yedongsoon/account_service/application/exception/CoupleException.kt @@ -4,4 +4,5 @@ import org.springframework.http.HttpStatus class CoupleNotFoundException(message: String) : AbstractException(HttpStatus.NOT_FOUND, message) class CoupleExistException(message: String) : AbstractException(HttpStatus.BAD_REQUEST, message) +class InvalidInviteCodeException(message: String) : AbstractException(HttpStatus.BAD_REQUEST, message) diff --git a/src/main/kotlin/com/yedongsoon/account_service/domain/member/model/CoupleCreateCommand.kt b/src/main/kotlin/com/yedongsoon/account_service/domain/member/model/CoupleCreateCommand.kt index 0ef1796..9b2b101 100644 --- a/src/main/kotlin/com/yedongsoon/account_service/domain/member/model/CoupleCreateCommand.kt +++ b/src/main/kotlin/com/yedongsoon/account_service/domain/member/model/CoupleCreateCommand.kt @@ -1,6 +1,7 @@ package com.yedongsoon.account_service.domain.member.model data class CoupleCreateCommand( - val accountNoA: Int, + val inviteCode:String, + val accountNoA: Int?=null, val accountNoB: Int, ) \ No newline at end of file diff --git a/src/main/kotlin/com/yedongsoon/account_service/infrastructure/RedisConfig.kt b/src/main/kotlin/com/yedongsoon/account_service/infrastructure/RedisConfig.kt new file mode 100644 index 0000000..7ed56e3 --- /dev/null +++ b/src/main/kotlin/com/yedongsoon/account_service/infrastructure/RedisConfig.kt @@ -0,0 +1,25 @@ +package com.yedongsoon.account_service.infrastructure + +import org.springframework.context.annotation.Bean +import org.springframework.context.annotation.Configuration +import org.springframework.data.redis.connection.ReactiveRedisConnectionFactory +import org.springframework.data.redis.core.ReactiveRedisTemplate +import org.springframework.data.redis.serializer.RedisSerializationContext +import org.springframework.data.redis.serializer.StringRedisSerializer + +@Configuration +class RedisConfig { + + @Bean + fun reactiveRedisTemplate(factory: ReactiveRedisConnectionFactory): ReactiveRedisTemplate { + val serializationContext = RedisSerializationContext + .newSerializationContext(StringRedisSerializer()) + .key(StringRedisSerializer()) + .value(StringRedisSerializer()) + .hashKey(StringRedisSerializer()) + .hashValue(StringRedisSerializer()) + .build() + + return ReactiveRedisTemplate(factory, serializationContext) + } +} \ No newline at end of file diff --git a/src/main/kotlin/com/yedongsoon/account_service/presentation/handler/CoupleHandler.kt b/src/main/kotlin/com/yedongsoon/account_service/presentation/handler/CoupleHandler.kt index 4f18078..ea7037c 100644 --- a/src/main/kotlin/com/yedongsoon/account_service/presentation/handler/CoupleHandler.kt +++ b/src/main/kotlin/com/yedongsoon/account_service/presentation/handler/CoupleHandler.kt @@ -59,4 +59,12 @@ class CoupleHandler( ServerResponse.ok().buildAndAwait() } + + suspend fun createInviteCode(request: ServerRequest): ServerResponse = withContext(Dispatchers.IO) { + val memberHeader = request.extractMemberCodeHeader() + + coupleCommandService.createInviteCode(memberHeader.no) + + ServerResponse.ok().buildAndAwait() + } } diff --git a/src/main/kotlin/com/yedongsoon/account_service/presentation/handler/model/CoupleCreateRequest.kt b/src/main/kotlin/com/yedongsoon/account_service/presentation/handler/model/CoupleCreateRequest.kt index f04903f..248e847 100644 --- a/src/main/kotlin/com/yedongsoon/account_service/presentation/handler/model/CoupleCreateRequest.kt +++ b/src/main/kotlin/com/yedongsoon/account_service/presentation/handler/model/CoupleCreateRequest.kt @@ -4,10 +4,10 @@ import com.yedongsoon.account_service.domain.member.model.CoupleCreateCommand data class CoupleCreateRequest( - val requestAccountNo: Int, + val inviteCode: String, ){ fun toCommand(memberNo:Int)=CoupleCreateCommand( - accountNoA = requestAccountNo, + inviteCode = inviteCode, accountNoB = memberNo, ) } \ No newline at end of file diff --git a/src/main/kotlin/com/yedongsoon/account_service/presentation/router/CoupleRouter.kt b/src/main/kotlin/com/yedongsoon/account_service/presentation/router/CoupleRouter.kt index 56d3cff..e94d532 100644 --- a/src/main/kotlin/com/yedongsoon/account_service/presentation/router/CoupleRouter.kt +++ b/src/main/kotlin/com/yedongsoon/account_service/presentation/router/CoupleRouter.kt @@ -19,6 +19,7 @@ class CoupleRouter(private val coupleHandler: CoupleHandler) { POST("", coupleHandler::createCouple) POST("/info", coupleHandler::createCoupleInfo) PUT("/info", coupleHandler::modifyCoupleInfo) + POST("/invite-code", coupleHandler::createInviteCode) } } } diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index f7858ce..b188087 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -26,5 +26,9 @@ spring: properties: hibernate: dialect: org.hibernate.dialect.MySQLDialect + data: + redis: + host: redis + port: 6379 server: port: 8030 \ No newline at end of file