캔 사용 시 국가 코드 기록 기능 추가

CloudFront-Viewer-Country 헤더를 통해 국가 코드를 수집하고 캔 사용 내역(UseCan) 저장 시 함께 기록하도록 수정
요청별 국가 정보 관리를 위한 컨텍스트와 인터셉터를 구현
This commit is contained in:
2026-01-09 11:51:42 +09:00
parent 68b5ed7cc2
commit 9b0d1b43d5
5 changed files with 52 additions and 6 deletions

View File

@@ -14,6 +14,7 @@ import kr.co.vividnext.sodalive.can.use.UseCanCalculateRepository
import kr.co.vividnext.sodalive.can.use.UseCanCalculateStatus
import kr.co.vividnext.sodalive.can.use.UseCanRepository
import kr.co.vividnext.sodalive.chat.character.image.CharacterImage
import kr.co.vividnext.sodalive.common.CountryContext
import kr.co.vividnext.sodalive.common.SodaException
import kr.co.vividnext.sodalive.content.AudioContent
import kr.co.vividnext.sodalive.content.order.Order
@@ -35,7 +36,8 @@ class CanPaymentService(
private val useCanRepository: UseCanRepository,
private val useCanCalculateRepository: UseCanCalculateRepository,
private val messageSource: SodaMessageSource,
private val langContext: LangContext
private val langContext: LangContext,
private val countryContext: CountryContext
) {
@Transactional
fun spendCan(
@@ -76,7 +78,8 @@ class CanPaymentService(
canUsage = canUsage,
can = useChargeCan?.total ?: 0,
rewardCan = useRewardCan.total,
isSecret = isSecret
isSecret = isSecret,
countryCode = countryContext.countryCode
)
var recipientId: Long? = null
@@ -378,7 +381,8 @@ class CanPaymentService(
canUsage = CanUsage.CHARACTER_IMAGE_PURCHASE,
can = useChargeCan?.total ?: 0,
rewardCan = useRewardCan.total,
isSecret = false
isSecret = false,
countryCode = countryContext.countryCode
)
useCan.member = member
useCan.characterImage = image
@@ -424,7 +428,8 @@ class CanPaymentService(
canUsage = CanUsage.CHAT_MESSAGE_PURCHASE,
can = useChargeCan?.total ?: 0,
rewardCan = useRewardCan.total,
isSecret = false
isSecret = false,
countryCode = countryContext.countryCode
)
useCan.member = member
useCan.chatMessage = message

View File

@@ -34,7 +34,9 @@ data class UseCan(
// 채팅 연동을 위한 식별자 (옵션)
var chatRoomId: Long? = null,
var characterId: Long? = null
var characterId: Long? = null,
var countryCode: String? = null
) : BaseEntity() {
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "member_id", nullable = false)

View File

@@ -0,0 +1,15 @@
package kr.co.vividnext.sodalive.common
import org.springframework.stereotype.Component
import org.springframework.web.context.annotation.RequestScope
@Component
@RequestScope
class CountryContext {
var countryCode: String? = null
internal set
fun setCountryCode(code: String?) {
this.countryCode = code
}
}

View File

@@ -0,0 +1,21 @@
package kr.co.vividnext.sodalive.common
import org.springframework.stereotype.Component
import org.springframework.web.servlet.HandlerInterceptor
import javax.servlet.http.HttpServletRequest
import javax.servlet.http.HttpServletResponse
@Component
class CountryInterceptor(
private val countryContext: CountryContext
) : HandlerInterceptor {
override fun preHandle(
request: HttpServletRequest,
response: HttpServletResponse,
handler: Any
): Boolean {
val countryCode = request.getHeader("CloudFront-Viewer-Country")
countryContext.setCountryCode(countryCode)
return true
}
}

View File

@@ -1,5 +1,6 @@
package kr.co.vividnext.sodalive.configs
import kr.co.vividnext.sodalive.common.CountryInterceptor
import kr.co.vividnext.sodalive.i18n.LangInterceptor
import org.springframework.context.annotation.Configuration
import org.springframework.web.servlet.config.annotation.CorsRegistry
@@ -8,10 +9,12 @@ import org.springframework.web.servlet.config.annotation.WebMvcConfigurer
@Configuration
class WebConfig(
private val langInterceptor: LangInterceptor
private val langInterceptor: LangInterceptor,
private val countryInterceptor: CountryInterceptor
) : WebMvcConfigurer {
override fun addInterceptors(registry: InterceptorRegistry) {
registry.addInterceptor(langInterceptor).addPathPatterns("/**")
registry.addInterceptor(countryInterceptor).addPathPatterns("/**")
}
override fun addCorsMappings(registry: CorsRegistry) {