Files
sodalive-backend-spring-boot/src/main/kotlin/kr/co/vividnext/sodalive/useraction/UserActionService.kt

189 lines
7.2 KiB
Kotlin

package kr.co.vividnext.sodalive.useraction
import kotlinx.coroutines.CoroutineExceptionHandler
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.cancel
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import kr.co.vividnext.sodalive.content.order.Order
import kr.co.vividnext.sodalive.content.order.OrderRepository
import kr.co.vividnext.sodalive.member.auth.AuthRepository
import kr.co.vividnext.sodalive.point.MemberPoint
import kr.co.vividnext.sodalive.point.MemberPointRepository
import kr.co.vividnext.sodalive.point.PointGrantLog
import kr.co.vividnext.sodalive.point.PointGrantLogRepository
import kr.co.vividnext.sodalive.point.PointRewardPolicyRepository
import org.slf4j.LoggerFactory
import org.springframework.stereotype.Service
import org.springframework.transaction.support.TransactionTemplate
import java.time.LocalDateTime
import javax.annotation.PreDestroy
@Service
class UserActionService(
private val repository: UserActionLogRepository,
private val orderRepository: OrderRepository,
private val policyRepository: PointRewardPolicyRepository,
private val grantLogRepository: PointGrantLogRepository,
private val memberPointRepository: MemberPointRepository,
private val transactionTemplate: TransactionTemplate,
private val authRepository: AuthRepository
) {
private val coroutineScope = CoroutineScope(
Dispatchers.IO + CoroutineExceptionHandler { _, e ->
logger.error("포인트 지급 또는 알림 실패: ${e.message}")
}
)
fun recordAction(
memberId: Long,
isAuth: Boolean,
actionType: ActionType,
contentId: Long? = null,
contentCommentId: Long? = null
) {
coroutineScope.launch {
var actionCount = 0
var grantedCount = 0
val order: Order?
var orderId: Long? = null
var point = 0
val now = LocalDateTime.now()
val policy = policyRepository.findByActionTypeAndIsActiveTrue(actionType, now)
if (policy != null) {
val policyType = policy.policyType
val todayAt15 = now.toLocalDate().atTime(15, 0)
val policyTypeDailyStartDate = if (now.toLocalTime().isBefore(todayAt15.toLocalTime())) {
now.toLocalDate().minusDays(1).atTime(15, 0)
} else {
todayAt15
}
val isValidPolicyTypeDailyAndDailyStartDateAfterPolicyStartDate =
policyType == PolicyType.DAILY && policyTypeDailyStartDate >= policy.startDate
order = if (contentId != null) {
orderRepository.findByMemberIdAndContentId(
memberId = memberId,
contentId = contentId,
createdAt = if (isValidPolicyTypeDailyAndDailyStartDateAfterPolicyStartDate) {
policyTypeDailyStartDate
} else {
policy.startDate
}
)
} else {
null
}
orderId = order?.id
if (actionType == ActionType.ORDER_CONTENT_COMMENT && order == null) return@launch
point = if (actionType == ActionType.ORDER_CONTENT_COMMENT && order != null) {
order.can
} else {
policy.pointAmount
}
actionCount = repository.countByMemberIdAndActionTypeAndCreatedAtBetween(
memberId = memberId,
actionType = actionType,
startDate = if (isValidPolicyTypeDailyAndDailyStartDateAfterPolicyStartDate) {
policyTypeDailyStartDate
} else {
policy.startDate
},
endDate = policy.endDate ?: LocalDateTime.now()
)
grantedCount = grantLogRepository.countByMemberIdAndPolicyIdAndStartDate(
memberId,
policy.id!!,
startDate = if (isValidPolicyTypeDailyAndDailyStartDateAfterPolicyStartDate) {
policyTypeDailyStartDate
} else {
policy.startDate
},
orderId = order?.id
)
}
withContext(Dispatchers.IO) {
transactionTemplate.execute {
repository.save(
UserActionLog(
memberId = memberId,
actionType = actionType,
contentCommentId = contentCommentId
)
)
repository.flush()
}
}
withContext(Dispatchers.IO) {
if (isAuth) {
try {
transactionTemplate.execute {
if (policy != null) {
if (actionCount + 1 < policy.threshold) return@execute
if (grantedCount >= policy.availableCount) return@execute
if (point > 0) {
grantLogRepository.save(
PointGrantLog(
memberId = memberId,
point = point,
actionType = actionType,
policyId = policy.id!!,
orderId = orderId
)
)
memberPointRepository.save(
MemberPoint(
memberId = memberId,
point = point,
actionType = actionType,
expiresAt = now.plusDays(3)
)
)
}
}
}
} catch (e: Exception) {
logger.warn("포인트 지급 또는 알림 실패: ${e.message}")
}
}
}
}
}
fun recordAction(
memberId: Long,
actionType: ActionType,
contentId: Long? = null,
contentCommentId: Long? = null
) {
recordAction(
memberId = memberId,
isAuth = authRepository.getAuthIdByMemberId(memberId) != null,
actionType = actionType,
contentId = contentId,
contentCommentId = contentCommentId
)
}
@PreDestroy
fun onDestroy() {
coroutineScope.cancel("UserActionService 종료")
}
companion object {
private val logger = LoggerFactory.getLogger(UserActionService::class.java)
}
}