유저 행동 데이터, 포인트 추가 #309
|
@ -13,6 +13,8 @@ import kr.co.vividnext.sodalive.member.notification.UpdateNotificationSettingReq
|
||||||
import kr.co.vividnext.sodalive.member.signUp.SignUpRequestV2
|
import kr.co.vividnext.sodalive.member.signUp.SignUpRequestV2
|
||||||
import kr.co.vividnext.sodalive.member.social.google.GoogleAuthService
|
import kr.co.vividnext.sodalive.member.social.google.GoogleAuthService
|
||||||
import kr.co.vividnext.sodalive.member.social.kakao.KakaoAuthService
|
import kr.co.vividnext.sodalive.member.social.kakao.KakaoAuthService
|
||||||
|
import kr.co.vividnext.sodalive.useraction.ActionType
|
||||||
|
import kr.co.vividnext.sodalive.useraction.UserActionService
|
||||||
import org.springframework.data.domain.Pageable
|
import org.springframework.data.domain.Pageable
|
||||||
import org.springframework.security.core.annotation.AuthenticationPrincipal
|
import org.springframework.security.core.annotation.AuthenticationPrincipal
|
||||||
import org.springframework.security.core.userdetails.User
|
import org.springframework.security.core.userdetails.User
|
||||||
|
@ -34,7 +36,8 @@ class MemberController(
|
||||||
private val service: MemberService,
|
private val service: MemberService,
|
||||||
private val kakaoAuthService: KakaoAuthService,
|
private val kakaoAuthService: KakaoAuthService,
|
||||||
private val googleAuthService: GoogleAuthService,
|
private val googleAuthService: GoogleAuthService,
|
||||||
private val trackingService: AdTrackingService
|
private val trackingService: AdTrackingService,
|
||||||
|
private val userActionService: UserActionService
|
||||||
) {
|
) {
|
||||||
@GetMapping("/check/email")
|
@GetMapping("/check/email")
|
||||||
fun checkEmail(@RequestParam email: String) = service.duplicateCheckEmail(email)
|
fun checkEmail(@RequestParam email: String) = service.duplicateCheckEmail(email)
|
||||||
|
@ -60,6 +63,11 @@ class MemberController(
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
userActionService.recordAction(
|
||||||
|
memberId = response.memberId,
|
||||||
|
actionType = ActionType.SIGN_UP
|
||||||
|
)
|
||||||
|
|
||||||
return ApiResponse.ok(message = "회원가입을 축하드립니다.", data = response.loginResponse)
|
return ApiResponse.ok(message = "회원가입을 축하드립니다.", data = response.loginResponse)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,5 @@
|
||||||
|
package kr.co.vividnext.sodalive.point
|
||||||
|
|
||||||
|
import org.springframework.data.jpa.repository.JpaRepository
|
||||||
|
|
||||||
|
interface MemberPointRepository : JpaRepository<MemberPoint, Long>
|
|
@ -0,0 +1,27 @@
|
||||||
|
package kr.co.vividnext.sodalive.point
|
||||||
|
|
||||||
|
import com.querydsl.jpa.impl.JPAQueryFactory
|
||||||
|
import kr.co.vividnext.sodalive.point.QPointGrantLog.pointGrantLog
|
||||||
|
import org.springframework.data.jpa.repository.JpaRepository
|
||||||
|
|
||||||
|
interface PointGrantLogRepository : JpaRepository<PointGrantLog, Long>, PointGrantLogQueryRepository
|
||||||
|
|
||||||
|
interface PointGrantLogQueryRepository {
|
||||||
|
fun existsByMemberIdAndPolicyId(memberId: Long, policyId: Long): Boolean
|
||||||
|
}
|
||||||
|
|
||||||
|
class PointGrantLogQueryRepositoryImpl(
|
||||||
|
private val queryFactory: JPAQueryFactory
|
||||||
|
) : PointGrantLogQueryRepository {
|
||||||
|
override fun existsByMemberIdAndPolicyId(memberId: Long, policyId: Long): Boolean {
|
||||||
|
return queryFactory
|
||||||
|
.select(pointGrantLog.id)
|
||||||
|
.from(pointGrantLog)
|
||||||
|
.where(
|
||||||
|
pointGrantLog.memberId.eq(memberId),
|
||||||
|
pointGrantLog.policyId.eq(policyId)
|
||||||
|
)
|
||||||
|
.fetch()
|
||||||
|
.isNotEmpty()
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,34 @@
|
||||||
|
package kr.co.vividnext.sodalive.point
|
||||||
|
|
||||||
|
import com.querydsl.jpa.impl.JPAQueryFactory
|
||||||
|
import kr.co.vividnext.sodalive.point.QPointRewardPolicy.pointRewardPolicy
|
||||||
|
import kr.co.vividnext.sodalive.useraction.ActionType
|
||||||
|
import org.springframework.data.jpa.repository.JpaRepository
|
||||||
|
import java.time.LocalDateTime
|
||||||
|
|
||||||
|
interface PointRewardPolicyRepository : JpaRepository<PointRewardPolicy, Long>, PointRewardPolicyQueryRepository
|
||||||
|
|
||||||
|
interface PointRewardPolicyQueryRepository {
|
||||||
|
fun findByActionTypeAndIsActiveTrue(actionType: ActionType, nowDateTime: LocalDateTime): PointRewardPolicy?
|
||||||
|
}
|
||||||
|
|
||||||
|
class PointRewardPolicyQueryRepositoryImpl(
|
||||||
|
private val queryFactory: JPAQueryFactory
|
||||||
|
) : PointRewardPolicyQueryRepository {
|
||||||
|
override fun findByActionTypeAndIsActiveTrue(
|
||||||
|
actionType: ActionType,
|
||||||
|
nowDateTime: LocalDateTime
|
||||||
|
): PointRewardPolicy? {
|
||||||
|
return queryFactory
|
||||||
|
.selectFrom(pointRewardPolicy)
|
||||||
|
.where(
|
||||||
|
pointRewardPolicy.isActive,
|
||||||
|
pointRewardPolicy.actionType.eq(actionType),
|
||||||
|
pointRewardPolicy.startDate.loe(nowDateTime),
|
||||||
|
pointRewardPolicy.endDate.goe(nowDateTime)
|
||||||
|
.or(pointRewardPolicy.endDate.isNull)
|
||||||
|
)
|
||||||
|
.orderBy(pointRewardPolicy.endDate.asc())
|
||||||
|
.fetchFirst()
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,39 @@
|
||||||
|
package kr.co.vividnext.sodalive.useraction
|
||||||
|
|
||||||
|
import com.querydsl.jpa.impl.JPAQueryFactory
|
||||||
|
import kr.co.vividnext.sodalive.useraction.QUserActionLog.userActionLog
|
||||||
|
import org.springframework.data.jpa.repository.JpaRepository
|
||||||
|
import java.time.LocalDateTime
|
||||||
|
|
||||||
|
interface UserActionLogRepository : JpaRepository<UserActionLog, Long>, UserActionLogQueryRepository
|
||||||
|
|
||||||
|
interface UserActionLogQueryRepository {
|
||||||
|
fun countByMemberIdAndActionTypeAndCreatedAtBetween(
|
||||||
|
memberId: Long,
|
||||||
|
actionType: ActionType,
|
||||||
|
startDate: LocalDateTime,
|
||||||
|
endDate: LocalDateTime
|
||||||
|
): Int
|
||||||
|
}
|
||||||
|
|
||||||
|
class UserActionLogQueryRepositoryImpl(
|
||||||
|
private val queryFactory: JPAQueryFactory
|
||||||
|
) : UserActionLogQueryRepository {
|
||||||
|
override fun countByMemberIdAndActionTypeAndCreatedAtBetween(
|
||||||
|
memberId: Long,
|
||||||
|
actionType: ActionType,
|
||||||
|
startDate: LocalDateTime,
|
||||||
|
endDate: LocalDateTime
|
||||||
|
): Int {
|
||||||
|
return queryFactory
|
||||||
|
.select(userActionLog.id)
|
||||||
|
.from(userActionLog)
|
||||||
|
.where(
|
||||||
|
userActionLog.memberId.eq(memberId)
|
||||||
|
.and(userActionLog.actionType.eq(actionType))
|
||||||
|
.and(userActionLog.createdAt.between(startDate, endDate))
|
||||||
|
)
|
||||||
|
.fetch()
|
||||||
|
.size
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,62 @@
|
||||||
|
package kr.co.vividnext.sodalive.useraction
|
||||||
|
|
||||||
|
import kotlinx.coroutines.CoroutineScope
|
||||||
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
|
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.springframework.stereotype.Service
|
||||||
|
import java.time.LocalDateTime
|
||||||
|
|
||||||
|
@Service
|
||||||
|
class UserActionService(
|
||||||
|
private val repository: UserActionLogRepository,
|
||||||
|
private val policyRepository: PointRewardPolicyRepository,
|
||||||
|
private val grantLogRepository: PointGrantLogRepository,
|
||||||
|
private val memberPointRepository: MemberPointRepository
|
||||||
|
) {
|
||||||
|
|
||||||
|
private val coroutineScope = CoroutineScope(Dispatchers.IO)
|
||||||
|
|
||||||
|
fun recordAction(memberId: Long, actionType: ActionType) {
|
||||||
|
coroutineScope.launch {
|
||||||
|
val now = LocalDateTime.now()
|
||||||
|
repository.save(UserActionLog(memberId, actionType))
|
||||||
|
|
||||||
|
val policy = policyRepository.findByActionTypeAndIsActiveTrue(actionType, now)
|
||||||
|
if (policy != null) {
|
||||||
|
val actionCount = repository.countByMemberIdAndActionTypeAndCreatedAtBetween(
|
||||||
|
memberId = memberId,
|
||||||
|
actionType = actionType,
|
||||||
|
startDate = policy.startDate,
|
||||||
|
endDate = policy.endDate ?: now
|
||||||
|
)
|
||||||
|
if (actionCount < policy.threshold) return@launch
|
||||||
|
|
||||||
|
val alreadyGranted = grantLogRepository.existsByMemberIdAndPolicyId(memberId, policy.id!!)
|
||||||
|
if (alreadyGranted) return@launch
|
||||||
|
|
||||||
|
memberPointRepository.save(
|
||||||
|
MemberPoint(
|
||||||
|
memberId = memberId,
|
||||||
|
point = policy.pointAmount,
|
||||||
|
actionType = actionType,
|
||||||
|
expiresAt = now.plusDays(3)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
grantLogRepository.save(
|
||||||
|
PointGrantLog(
|
||||||
|
memberId = memberId,
|
||||||
|
point = policy.pointAmount,
|
||||||
|
actionType = actionType,
|
||||||
|
policyId = policy.id!!
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue